home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / vm / vm-folder.el.z / vm-folder.el
Encoding:
Text File  |  1998-05-21  |  136.8 KB  |  3,882 lines

  1. ;;; VM folder related functions
  2. ;;; Copyright (C) 1989-1998 Kyle E. Jones
  3. ;;;
  4. ;;; This program is free software; you can redistribute it and/or modify
  5. ;;; it under the terms of the GNU General Public License as published by
  6. ;;; the Free Software Foundation; either version 1, or (at your option)
  7. ;;; any later version.
  8. ;;;
  9. ;;; This program is distributed in the hope that it will be useful,
  10. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. ;;; GNU General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU General Public License
  15. ;;; along with this program; if not, write to the Free Software
  16. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. (provide 'vm-folder)
  19.  
  20. (defun vm-number-messages (&optional start-point end-point)
  21.   "Set the number-of and padded-number-of slots of messages
  22. in vm-message-list.
  23.  
  24. If non-nil, START-POINT should point to a cons cell in
  25. vm-message-list and the numbering will begin there, else the
  26. numbering will begin at the head of vm-message-list.  If
  27. START-POINT is non-nil the reverse-link-of slot of the message in
  28. the cons must be valid and the message pointed to (if any) must
  29. have a non-nil number-of slot, because it is used to determine
  30. what the starting message number should be.
  31.  
  32. If non-nil, END-POINT should point to a cons cell in
  33. vm-message-list and the numbering will end with the message just
  34. before this cell.  A nil value means numbering will be done until
  35. the end of vm-message-list is reached."
  36.   (let ((n 1) (message-list (or start-point vm-message-list)))
  37.     (if (and start-point (vm-reverse-link-of (car start-point)))
  38.     (setq n (1+ (string-to-int
  39.              (vm-number-of
  40.               (car
  41.                (vm-reverse-link-of
  42.             (car start-point))))))))
  43.     (while (not (eq message-list end-point))
  44.       (vm-set-number-of (car message-list) (int-to-string n))
  45.       (vm-set-padded-number-of (car message-list) (format "%3d" n))
  46.       (setq n (1+ n) message-list (cdr message-list)))
  47.     (or end-point (setq vm-ml-highest-message-number (int-to-string (1- n))))
  48.     (if vm-summary-buffer
  49.     (vm-copy-local-variables vm-summary-buffer
  50.                  'vm-ml-highest-message-number))))
  51.  
  52. (defun vm-set-numbering-redo-start-point (start-point)
  53.   "Set vm-numbering-redo-start-point to START-POINT if appropriate.
  54. Also mark the current buffer as needing a display update.
  55.  
  56. START-POINT should be a cons in vm-message-list or just t.
  57.  (t means start from the beginning of vm-message-list.)
  58. If START-POINT is closer to the head of vm-message-list than
  59. vm-numbering-redo-start-point or is equal to t, then
  60. vm-numbering-redo-start-point is set to match it."
  61.   (intern (buffer-name) vm-buffers-needing-display-update)
  62.   (if (eq vm-numbering-redo-start-point t)
  63.       nil
  64.     (if (and (consp start-point) (consp vm-numbering-redo-start-point))
  65.     (let ((mp vm-message-list))
  66.       (while (and mp (not (or (eq mp start-point)
  67.                   (eq mp vm-numbering-redo-start-point))))
  68.         (setq mp (cdr mp)))
  69.       (if (null mp)
  70.           (error "Something is wrong in vm-set-numbering-redo-start-point"))
  71.       (if (eq mp start-point)
  72.           (setq vm-numbering-redo-start-point start-point)))
  73.       (setq vm-numbering-redo-start-point start-point))))
  74.  
  75. (defun vm-set-numbering-redo-end-point (end-point)
  76.   "Set vm-numbering-redo-end-point to END-POINT if appropriate.
  77. Also mark the current buffer as needing a display update.
  78.  
  79. END-POINT should be a cons in vm-message-list or just t.
  80.  (t means number all the way to the end of vm-message-list.)
  81. If END-POINT is closer to the end of vm-message-list or is equal
  82. to t, then vm-numbering-redo-start-point is set to match it.
  83. The number-of slot is used to determine proximity to the end of
  84. vm-message-list, so this slot must be valid in END-POINT's message
  85. and the message in the cons pointed to by vm-numbering-redo-end-point."
  86.   (intern (buffer-name) vm-buffers-needing-display-update)
  87.   (cond ((eq end-point t)
  88.      (setq vm-numbering-redo-end-point t))
  89.     ((and (consp end-point)
  90.           (> (string-to-int
  91.           (vm-number-of
  92.            (car end-point)))
  93.          (string-to-int
  94.           (vm-number-of
  95.            (car vm-numbering-redo-end-point)))))
  96.      (setq vm-numbering-redo-end-point end-point))
  97.     ((null end-point)
  98.      (setq vm-numbering-redo-end-point end-point))))
  99.  
  100. (defun vm-do-needed-renumbering ()
  101.   "Number messages in vm-message-list as specified by
  102. vm-numbering-redo-start-point and vm-numbering-redo-end-point.
  103.  
  104. vm-numbering-redo-start-point = t means start at the head
  105. of vm-message-list.
  106. vm-numbering-redo-end-point = t means number all the way to the
  107. end of vm-message-list.
  108.  
  109. Otherwise the variables' values should be conses in vm-message-list
  110. or nil."
  111.   (if vm-numbering-redo-start-point
  112.       (progn
  113.     (vm-number-messages (and (consp vm-numbering-redo-start-point)
  114.                  vm-numbering-redo-start-point)
  115.                 vm-numbering-redo-end-point)
  116.     (setq vm-numbering-redo-start-point nil
  117.           vm-numbering-redo-end-point nil))))
  118.  
  119. (defun vm-set-summary-redo-start-point (start-point)
  120.   "Set vm-summary-redo-start-point to START-POINT if appropriate.
  121. Also mark the current buffer as needing a display update.
  122.  
  123. START-POINT should be a cons in vm-message-list or just t.
  124.  (t means start from the beginning of vm-message-list.)
  125. If START-POINT is closer to the head of vm-message-list than
  126. vm-summary-redo-start-point or is equal to t, then
  127. vm-summary-redo-start-point is set to match it."
  128.   (intern (buffer-name) vm-buffers-needing-display-update)
  129.   (if (eq vm-summary-redo-start-point t)
  130.       nil
  131.     (if (and (consp start-point) (consp vm-summary-redo-start-point))
  132.     (let ((mp vm-message-list))
  133.       (while (and mp (not (or (eq mp start-point)
  134.                   (eq mp vm-summary-redo-start-point))))
  135.         (setq mp (cdr mp)))
  136.       (if (null mp)
  137.           (error "Something is wrong in vm-set-summary-redo-start-point"))
  138.       (if (eq mp start-point)
  139.           (setq vm-summary-redo-start-point start-point)))
  140.       (setq vm-summary-redo-start-point start-point))))
  141.  
  142. (defun vm-mark-for-summary-update (m &optional dont-kill-cache)
  143.   "Mark message M for a summary update.
  144. Also mark M's buffer as needing a display update. Any virtual
  145. messages of M and their buffers are similarly marked for update.
  146. If M is a virtual message and virtual mirroring is in effect for
  147. M (i.e. attribute-of eq attributes-of M's real message), M's real
  148. message and its buffer are scheduled for an update.
  149.  
  150. Optional arg DONT-KILL-CACHE non-nil means don't invalidate the
  151. summary-of slot for any messages marked for update.  This is
  152. meant to be used by functions that update message information
  153. that is not cached in the summary-of slot, e.g. message numbers
  154. and thread indentation."
  155.   (cond ((eq m (vm-real-message-of m))
  156.      ;; this is a real message.
  157.      ;; its summary and modeline need to be updated.
  158.      (if (not dont-kill-cache)
  159.          ;; toss the cache.  this also tosses the cache of any
  160.          ;; virtual messages mirroring this message.  the summary
  161.          ;; entry cache must be cleared when an attribute of a
  162.          ;; message that could appear in the summary has changed.
  163.          (vm-set-summary-of m nil))
  164.      (if (vm-su-start-of m)
  165.          (setq vm-messages-needing-summary-update 
  166.            (cons m vm-messages-needing-summary-update)))
  167.      (intern (buffer-name (vm-buffer-of m))
  168.          vm-buffers-needing-display-update)
  169.      ;; find the virtual messages of this real message that
  170.      ;; need a summary update.
  171.      (let ((m-list (vm-virtual-messages-of m)))
  172.        (while m-list
  173.          (if (eq (vm-attributes-of m) (vm-attributes-of (car m-list)))
  174.          (progn
  175.            (and (vm-su-start-of (car m-list))
  176.             (setq vm-messages-needing-summary-update
  177.                   (cons (car m-list)
  178.                     vm-messages-needing-summary-update)))
  179.            (intern (buffer-name (vm-buffer-of (car m-list)))
  180.                vm-buffers-needing-display-update)))
  181.          (setq m-list (cdr m-list)))))
  182.     (t
  183.      ;; this is a virtual message.
  184.      ;;
  185.      ;; if this message has virtual messages then we need to
  186.      ;; schedule updates for all the virtual messages that
  187.      ;; share a cache with this message and we need to
  188.      ;; schedule an update for the underlying real message
  189.      ;; since we are mirroring it.
  190.      ;;
  191.      ;; if there are no virtual messages, then this virtual
  192.      ;; message is not mirroring its real message so we need
  193.      ;; only take care of this one message.
  194.      (if (vm-virtual-messages-of m)
  195.          (let ((m-list (vm-virtual-messages-of m)))
  196.            ;; schedule updates for all the virtual message who share
  197.            ;; the same cache as this message.
  198.            (while m-list
  199.          (if (eq (vm-attributes-of m) (vm-attributes-of (car m-list)))
  200.              (progn
  201.                (and (vm-su-start-of (car m-list))
  202.                 (setq vm-messages-needing-summary-update
  203.                   (cons (car m-list)
  204.                     vm-messages-needing-summary-update)))
  205.                (intern (buffer-name (vm-buffer-of (car m-list)))
  206.                    vm-buffers-needing-display-update)))
  207.          (setq m-list (cdr m-list)))
  208.            ;; now take care of the real message
  209.            (if (not dont-kill-cache)
  210.            ;; toss the cache.  this also tosses the cache of
  211.            ;; any virtual messages sharing the same cache as
  212.            ;; this message.
  213.            (vm-set-summary-of m nil))
  214.            (and (vm-su-start-of (vm-real-message-of m))
  215.             (setq vm-messages-needing-summary-update
  216.               (cons (vm-real-message-of m)
  217.                 vm-messages-needing-summary-update)))
  218.            (intern (buffer-name (vm-buffer-of (vm-real-message-of m)))
  219.                vm-buffers-needing-display-update))
  220.        (if (not dont-kill-cache)
  221.            (vm-set-virtual-summary-of m nil))
  222.        (and (vm-su-start-of m)
  223.         (setq vm-messages-needing-summary-update
  224.               (cons m vm-messages-needing-summary-update)))
  225.        (intern (buffer-name (vm-buffer-of m))
  226.            vm-buffers-needing-display-update)))))
  227.  
  228. (defun vm-force-mode-line-update ()
  229.   "Force a mode line update in all frames."
  230.   (if (fboundp 'force-mode-line-update)
  231.       (force-mode-line-update t)
  232.     (save-excursion
  233.       (set-buffer (other-buffer))
  234.       (set-buffer-modified-p (buffer-modified-p)))))
  235.  
  236. (defun vm-do-needed-mode-line-update ()
  237.   "Do a modeline update for the current folder buffer.
  238. This means setting up all the various vm-ml attribute variables
  239. in the folder buffer and copying necessary variables to the
  240. folder buffer's summary and presentation buffers, and then
  241. forcing Emacs to update all modelines.
  242.  
  243. If a virtual folder being updated has no messages, then
  244. erase-buffer is called on its buffer.
  245.  
  246. If any type of folder is empty, erase-buffer is called
  247. on its presentation buffer, if any."
  248.   ;; XXX This last bit should probably should be moved to
  249.   ;; XXX vm-expunge-folder.
  250.  
  251.   (if (null vm-message-pointer)
  252.       (progn
  253.     ;; erase the leftover message if the folder is really empty.
  254.     (if (eq major-mode 'vm-virtual-mode)
  255.         (let ((buffer-read-only nil)
  256.           (omodified (buffer-modified-p)))
  257.           (unwind-protect
  258.           (erase-buffer)
  259.         (set-buffer-modified-p omodified))))
  260.     (if vm-presentation-buffer
  261.         (let ((omodified (buffer-modified-p)))
  262.           (unwind-protect
  263.           (save-excursion
  264.             (set-buffer vm-presentation-buffer)
  265.             (let ((buffer-read-only nil))
  266.               (erase-buffer)))
  267.         (set-buffer-modified-p omodified)))))
  268.     ;; try to avoid calling vm-su-labels if possible so as to
  269.     ;; avoid loading vm-summary.el.
  270.     (if (vm-labels-of (car vm-message-pointer))
  271.     (setq vm-ml-labels (vm-su-labels (car vm-message-pointer)))
  272.       (setq vm-ml-labels nil))
  273.     (setq vm-ml-message-number (vm-number-of (car vm-message-pointer)))
  274.     (setq vm-ml-message-new (vm-new-flag (car vm-message-pointer)))
  275.     (setq vm-ml-message-unread (vm-unread-flag (car vm-message-pointer)))
  276.     (setq vm-ml-message-read
  277.       (and (not (vm-new-flag (car vm-message-pointer)))
  278.            (not (vm-unread-flag (car vm-message-pointer)))))
  279.     (setq vm-ml-message-edited (vm-edited-flag (car vm-message-pointer)))
  280.     (setq vm-ml-message-filed (vm-filed-flag (car vm-message-pointer)))
  281.     (setq vm-ml-message-written (vm-written-flag (car vm-message-pointer)))
  282.     (setq vm-ml-message-replied (vm-replied-flag (car vm-message-pointer)))
  283.     (setq vm-ml-message-forwarded (vm-forwarded-flag (car vm-message-pointer)))
  284.     (setq vm-ml-message-redistributed (vm-redistributed-flag (car vm-message-pointer)))
  285.     (setq vm-ml-message-deleted (vm-deleted-flag (car vm-message-pointer)))
  286.     (setq vm-ml-message-marked (vm-mark-of (car vm-message-pointer))))
  287.   (if vm-summary-buffer
  288.       (let ((modified (buffer-modified-p)))
  289.     (save-excursion
  290.       (vm-copy-local-variables vm-summary-buffer
  291.                    'default-directory
  292.                    'vm-ml-message-new
  293.                    'vm-ml-message-unread
  294.                    'vm-ml-message-read
  295.                    'vm-ml-message-edited
  296.                    'vm-ml-message-replied
  297.                    'vm-ml-message-forwarded
  298.                    'vm-ml-message-filed
  299.                    'vm-ml-message-written
  300.                    'vm-ml-message-deleted
  301.                    'vm-ml-message-marked
  302.                    'vm-ml-message-number
  303.                    'vm-ml-highest-message-number
  304.                    'vm-folder-read-only
  305.                    'vm-folder-type
  306.                    'vm-virtual-folder-definition
  307.                    'vm-virtual-mirror
  308.                    'vm-ml-sort-keys
  309.                    'vm-ml-labels
  310.                    'vm-spooled-mail-waiting
  311.                    'vm-message-list)
  312.       (set-buffer vm-summary-buffer)
  313.       (set-buffer-modified-p modified))))
  314.   (if vm-presentation-buffer
  315.       (let ((modified (buffer-modified-p)))
  316.     (save-excursion
  317.       (vm-copy-local-variables vm-presentation-buffer
  318.                    'default-directory
  319.                    'vm-ml-message-new
  320.                    'vm-ml-message-unread
  321.                    'vm-ml-message-read
  322.                    'vm-ml-message-edited
  323.                    'vm-ml-message-replied
  324.                    'vm-ml-message-forwarded
  325.                    'vm-ml-message-filed
  326.                    'vm-ml-message-written
  327.                    'vm-ml-message-deleted
  328.                    'vm-ml-message-marked
  329.                    'vm-ml-message-number
  330.                    'vm-ml-highest-message-number
  331.                    'vm-folder-read-only
  332.                    'vm-folder-type
  333.                    'vm-virtual-folder-definition
  334.                    'vm-virtual-mirror
  335.                    'vm-ml-labels
  336.                    'vm-spooled-mail-waiting
  337.                    'vm-message-list)
  338.       (set-buffer vm-presentation-buffer)
  339.       (set-buffer-modified-p modified))))
  340.   (vm-force-mode-line-update))
  341.  
  342. (defun vm-update-summary-and-mode-line ()
  343.   "Update summary and mode line for all VM folder and summary buffers.
  344. Really this updates all the visible status indicators.
  345.  
  346. Message lists are renumbered.
  347. Summary entries are wiped and regenerated.
  348. Mode lines are updated.
  349. Toolbars are updated."
  350.   (save-excursion
  351.     (mapatoms (function
  352.            (lambda (b)
  353.          (setq b (get-buffer (symbol-name b)))
  354.          (if b
  355.              (progn
  356.                (set-buffer b)
  357.                (vm-check-for-killed-summary)
  358.                (and vm-use-toolbar
  359.                 (vm-toolbar-support-possible-p)
  360.                 (vm-toolbar-update-toolbar))
  361.                (vm-do-needed-renumbering)
  362.                (if vm-summary-buffer
  363.                (vm-do-needed-summary-rebuild))
  364.                (vm-do-needed-mode-line-update)))))
  365.           vm-buffers-needing-display-update)
  366.     (fillarray vm-buffers-needing-display-update 0))
  367.   (if vm-messages-needing-summary-update
  368.       (progn
  369.     (mapcar (function vm-update-message-summary)
  370.         vm-messages-needing-summary-update)
  371.     (setq vm-messages-needing-summary-update nil)))
  372.   (vm-force-mode-line-update))
  373.  
  374. (defun vm-reverse-link-messages ()
  375.   "Set reverse links for all messages in vm-message-list."
  376.   (let ((mp vm-message-list)
  377.     (prev nil))
  378.     (while mp
  379.       (vm-set-reverse-link-of (car mp) prev)
  380.       (setq prev mp mp (cdr mp)))))
  381.  
  382. (defun vm-match-ordered-header (alist)
  383.   "Try to match a header in ALIST and return the matching cell.
  384. This is used by header ordering code.
  385.  
  386. ALIST looks like this ((\"From\") (\"To\")).  This function returns
  387. the alist element whose car matches the header starting at point.
  388. The header ordering code uses the cdr of the element
  389. returned to hold headers to be output later."
  390.   (let ((case-fold-search t))
  391.     (catch 'match
  392.       (while alist
  393.     (if (looking-at (car (car alist)))
  394.         (throw 'match (car alist)))
  395.     (setq alist (cdr alist)))
  396.       nil)))
  397.  
  398. (defun vm-match-header (&optional header-name)
  399.   "Match a header and save some state information about the matched header.
  400. Optional first arg HEADER-NAME means match the header only
  401. if it matches HEADER-NAME.  HEADER-NAME should be a string
  402. containing a header name.  The string should end with a colon if just
  403. that name should be matched.  A string that does not end in a colon
  404. will match all headers that begin with that string.
  405.  
  406. State information is stored in vm-matched-header-vector bound to a vector
  407. of this form.
  408.  
  409.  [ header-start header-end
  410.    header-name-start header-name-end
  411.    header-contents-start header-contents-end ]
  412.  
  413. Elements are integers.
  414. There are functions to access and use this info."
  415.   (let ((case-fold-search t)
  416.     (header-name-regexp "\\([^ \t\n:]+\\):"))
  417.     (if (if header-name
  418.         (and (looking-at header-name) (looking-at header-name-regexp))
  419.       (looking-at header-name-regexp))
  420.     (save-excursion
  421.       (aset vm-matched-header-vector 0 (point))
  422.       (aset vm-matched-header-vector 2 (point))
  423.       (aset vm-matched-header-vector 3 (match-end 1))
  424.       (goto-char (match-end 0))
  425.       ;; skip leading whitespace
  426.       (skip-chars-forward " \t")
  427.       (aset vm-matched-header-vector 4 (point))
  428.       (forward-line 1)
  429.       (while (looking-at "[ \t]")
  430.         (forward-line 1))
  431.       (aset vm-matched-header-vector 1 (point))
  432.       ;; drop the trailing newline
  433.       (aset vm-matched-header-vector 5 (1- (point)))))))
  434.  
  435. (defun vm-matched-header ()
  436.   "Returns the header last matched by vm-match-header.
  437. Trailing newline is included."
  438.   (vm-buffer-substring-no-properties (aref vm-matched-header-vector 0)
  439.                      (aref vm-matched-header-vector 1)))
  440.  
  441. (defun vm-matched-header-name ()
  442.   "Returns the name of the header last matched by vm-match-header."
  443.   (vm-buffer-substring-no-properties (aref vm-matched-header-vector 2)
  444.                      (aref vm-matched-header-vector 3)))
  445.  
  446. (defun vm-matched-header-contents ()
  447.   "Returns the contents of the header last matched by vm-match-header.
  448. Trailing newline is not included."
  449.   (vm-buffer-substring-no-properties (aref vm-matched-header-vector 4)
  450.                      (aref vm-matched-header-vector 5)))
  451.  
  452. (defun vm-matched-header-start ()
  453.   "Returns the start position of the header last matched by vm-match-header."
  454.   (aref vm-matched-header-vector 0))
  455.  
  456. (defun vm-matched-header-end ()
  457.   "Returns the end position of the header last matched by vm-match-header."
  458.   (aref vm-matched-header-vector 1))
  459.  
  460. (defun vm-matched-header-name-start ()
  461.   "Returns the start position of the name of the header last matched
  462. by vm-match-header."
  463.   (aref vm-matched-header-vector 2))
  464.  
  465. (defun vm-matched-header-name-end ()
  466.   "Returns the end position of the name of the header last matched
  467. by vm-match-header."
  468.   (aref vm-matched-header-vector 3))
  469.  
  470. (defun vm-matched-header-contents-start ()
  471.   "Returns the start position of the contents of the header last matched
  472. by vm-match-header."
  473.   (aref vm-matched-header-vector 4))
  474.  
  475. (defun vm-matched-header-contents-end ()
  476.   "Returns the end position of the contents of the header last matched
  477. by vm-match-header."
  478.   (aref vm-matched-header-vector 5))
  479.  
  480. (defun vm-get-folder-type (&optional file start end)
  481.   "Return a symbol indicating the folder type of the current buffer.
  482. This function works by examining the beginning of a folder.
  483. If optional arg FILE is present the type of FILE is returned instead.
  484. If optional second and third arg START and END are provided,
  485. vm-get-folder-type will examine the text between those buffer
  486. positions.  START and END default to 1 and (buffer-size) + 1.
  487.  
  488. Returns
  489.   nil      if folder has no type (empty)
  490.   unknown  if the type is not known to VM
  491.   mmdf     for MMDF folders
  492.   babyl    for BABYL folders
  493.   From_    for UNIX From_ folders
  494.  
  495. If vm-trust-From_-with-Content-Length is non-nil,
  496. From_-with-Content-Length is returned if the first message in the
  497. folder has a Content-Length header and the folder otherwise looks
  498. like a From_ folder."
  499.   (let ((temp-buffer nil)
  500.     b
  501.     (case-fold-search nil))
  502.     (unwind-protect
  503.     (save-excursion
  504.       (if file
  505.           (progn
  506.         (setq b (vm-get-file-buffer file))
  507.         (if b
  508.             (set-buffer b)
  509.           (setq temp-buffer (generate-new-buffer "*vm-work*"))
  510.           (set-buffer temp-buffer)
  511.           (if (file-readable-p file)
  512.               (condition-case nil
  513.               (let ((coding-system-for-read 'binary))
  514.                 (insert-file-contents file nil 0 4096))
  515.             (wrong-number-of-arguments
  516.              (call-process "sed" file temp-buffer nil
  517.                        "-n" "1,/^$/p")))))))
  518.       (save-excursion
  519.         (save-restriction
  520.           (or start (setq start 1))
  521.           (or end (setq end (1+ (buffer-size))))
  522.           (widen)
  523.           (narrow-to-region start end)
  524.           (goto-char (point-min))
  525.           (cond ((zerop (buffer-size)) nil)
  526.             ((looking-at "\n*From ")
  527.              (if (not vm-trust-From_-with-Content-Length)
  528.              'From_
  529.                (let ((case-fold-search t))
  530.              (re-search-forward vm-content-length-search-regexp
  531.                         nil t))
  532.                (cond ((match-beginning 1)
  533.                   'From_)
  534.                  ((match-beginning 0)
  535.                   'From_-with-Content-Length)
  536.                  (t 'From_))))
  537.             ((looking-at "\001\001\001\001\n") 'mmdf)
  538.             ((looking-at "BABYL OPTIONS:") 'babyl)
  539.             (t 'unknown)))))
  540.       (and temp-buffer (kill-buffer temp-buffer)))))
  541.  
  542. (defun vm-convert-folder-type (old-type new-type)
  543.   "Convert buffer from OLD-TYPE to NEW-TYPE.
  544. OLD-TYPE and NEW-TYPE should be symbols returned from vm-get-folder-type.
  545. This should be called on non-live buffers like crash boxes.
  546. This will confuse VM if called on a folder buffer in vm-mode."
  547.   (let ((vm-folder-type old-type)
  548.     (pos-list nil)
  549.     beg end)
  550.     (goto-char (point-min))
  551.     (vm-skip-past-folder-header)
  552.     (while (vm-find-leading-message-separator)
  553.       (setq pos-list (cons (point-marker) pos-list))
  554.       (vm-skip-past-leading-message-separator)
  555.       (setq pos-list (cons (point-marker) pos-list))
  556.       (vm-find-trailing-message-separator)
  557.       (setq pos-list (cons (point-marker) pos-list))
  558.       (vm-skip-past-trailing-message-separator)
  559.       (setq pos-list (cons (point-marker) pos-list)))
  560.     (setq pos-list (nreverse pos-list))
  561.     (goto-char (point-min))
  562.     (vm-convert-folder-header old-type new-type)
  563.     (while pos-list
  564.       (setq beg (car pos-list))
  565.       (goto-char (car pos-list))
  566.       (insert-before-markers (vm-leading-message-separator new-type))
  567.       (delete-region (car pos-list) (car (cdr pos-list)))
  568.       (vm-convert-folder-type-headers old-type new-type)
  569.       (setq pos-list (cdr (cdr pos-list)))
  570.       (setq end (marker-position (car pos-list)))
  571.       (goto-char (car pos-list))
  572.       (insert-before-markers (vm-trailing-message-separator new-type))
  573.       (delete-region (car pos-list) (car (cdr pos-list)))
  574.       (goto-char beg)
  575.       (vm-munge-message-separators new-type beg end)
  576.       (setq pos-list (cdr (cdr pos-list))))))
  577.  
  578. (defun vm-convert-folder-header (old-type new-type)
  579.   "Convert the folder header form OLD-TYPE to NEW-TYPE.
  580. The folder header is the text at the beginning of a folder that
  581. is a legal part of the folder but is not part of the first
  582. message.  This is for dealing with BABYL files."
  583.   (if (eq old-type 'babyl)
  584.       (save-excursion
  585.     (let ((beg (point))
  586.           (case-fold-search t))
  587.       (cond ((and (looking-at "BABYL OPTIONS:")
  588.               (search-forward "\037" nil t))
  589.          (delete-region beg (point)))))))
  590.   (if (eq new-type 'babyl)
  591.       ;; insert before markers so that message location markers
  592.       ;; for the first message get moved forward.
  593.       (insert-before-markers "BABYL OPTIONS:\nVersion: 5\n\037")))
  594.  
  595. (defun vm-skip-past-folder-header ()
  596.   "Move point past the folder header.
  597. The folder header is the text at the beginning of a folder that
  598. is a legal part of the folder but is not part of the first
  599. message.  This is for dealing with BABYL files."
  600.   (cond ((eq vm-folder-type 'babyl)
  601.      (search-forward "\037" nil 0))))
  602.  
  603. (defun vm-convert-folder-type-headers (old-type new-type)
  604.   "Convert headers in the message around point from OLD-TYPE to NEW-TYPE.
  605. This means to add/delete Content-Length and any other
  606. headers related to folder-type as needed for folder type
  607. conversions.  This function expects point to be at the beginning
  608. of the header section of a message, and it only deals with that
  609. message."
  610.   (let (length)
  611.     ;; get the length now before the content-length headers are
  612.     ;; removed.
  613.     (if (eq new-type 'From_-with-Content-Length)
  614.     (let (start)
  615.       (save-excursion
  616.         (save-excursion
  617.           (search-forward "\n\n" nil 0)
  618.           (setq start (point)))
  619.         (let ((vm-folder-type old-type))
  620.           (vm-find-trailing-message-separator))
  621.         (setq length (- (point) start)))))
  622.     ;; chop out content-length header if new format doesn't need
  623.     ;; it or if the new format computed his own copy.
  624.     (if (or (eq old-type 'From_-with-Content-Length)
  625.         (eq new-type 'From_-with-Content-Length))
  626.     (save-excursion
  627.       (while (and (let ((case-fold-search t))
  628.             (re-search-forward vm-content-length-search-regexp
  629.                        nil t))
  630.               (null (match-beginning 1))
  631.               (progn (goto-char (match-beginning 0))
  632.                  (vm-match-header vm-content-length-header)))
  633.         (delete-region (vm-matched-header-start)
  634.                (vm-matched-header-end)))))
  635.     ;; insert the content-length header if needed
  636.     (if (eq new-type 'From_-with-Content-Length)
  637.     (save-excursion
  638.       (insert vm-content-length-header " " (int-to-string length) "\n")))))
  639.  
  640. (defun vm-munge-message-separators (folder-type start end)
  641.   "Munge message separators of FOLDER-TYPE found between START and END.
  642. This function is used to eliminate message separators for a particular
  643. folder type that happen to occur in a message.  \">\" is prepended to such
  644. separators."
  645.   (save-excursion
  646.     (let ((vm-folder-type folder-type))
  647.       (cond ((memq folder-type '(From_ From_-with-Content-Length mmdf babyl))
  648.          (setq end (vm-marker end))
  649.          (goto-char start)
  650.          (while (and (vm-find-leading-message-separator)
  651.              (< (point) end))
  652.            (insert ">"))
  653.          (set-marker end nil))))))
  654.  
  655. (defun vm-compatible-folder-p (file)
  656.   "Return non-nil if FILE is a compatible folder with the current buffer.
  657. The current folder must have vm-folder-type initialized.
  658. FILE is compatible if
  659.   - it is empty
  660.   - the current folder is empty
  661.   - the two folder types are equal"
  662.   (let ((type (vm-get-folder-type file)))
  663.     (or (not (and vm-folder-type type))
  664.     (eq vm-folder-type type))))
  665.  
  666. (defun vm-leading-message-separator (&optional folder-type message
  667.                      for-other-folder)
  668.   "Returns a leading message separator for the current folder.
  669. Defaults to returning a separator for the current folder type.
  670.  
  671. Optional first arg FOLDER-TYPE means return a separator for that
  672. folder type instead.
  673.  
  674. Optional second arg MESSAGE should be a message struct.  This is used
  675. generating BABYL separators, because they contain message attributes
  676. and labels that must must be copied from the message.
  677.  
  678. Optional third arg FOR-OTHER-FOLDER non-nil means that this separator will
  679. be used a `foreign' folder.  This means that the `deleted'
  680. attributes should not be copied for BABYL folders."
  681.   (let ((type (or folder-type vm-folder-type)))
  682.     (cond ((memq type '(From_ From_-with-Content-Length))
  683.        (concat "From VM " (current-time-string) "\n"))
  684.       ((eq type 'mmdf)
  685.        "\001\001\001\001\n")
  686.       ((eq type 'babyl)
  687.        (cond (message
  688.           (concat "\014\n0,"
  689.               (vm-babyl-attributes-string message for-other-folder)
  690.               ",\n*** EOOH ***\n"))
  691.          (t "\014\n0, recent, unseen,,\n*** EOOH ***\n"))))))
  692.  
  693. (defun vm-trailing-message-separator (&optional folder-type)
  694.   "Returns a leading message separator for the current folder.
  695. Defaults to returning a separator for the current folder type.
  696.  
  697. Optional first arg FOLDER-TYPE means return a separator for that
  698. folder type instead."
  699.   (let ((type (or folder-type vm-folder-type)))
  700.     (cond ((eq type 'From_) "\n")
  701.       ((eq type 'From_-with-Content-Length) "")
  702.       ((eq type 'mmdf) "\001\001\001\001\n")
  703.       ((eq type 'babyl) "\037"))))
  704.  
  705. (defun vm-folder-header (&optional folder-type label-obarray)
  706.   "Returns a folder header for the current folder.
  707. Defaults to returning a folder header for the current folder type.
  708.  
  709. Optional first arg FOLDER-TYPE means return a folder header for that
  710. folder type instead.
  711.  
  712. Optional second arg LABEL-OBARRAY should be an obarray of labels
  713. that have been used in this folder.  This is used for BABYL folders."
  714.   (let ((type (or folder-type vm-folder-type)))
  715.     (cond ((eq type 'babyl)
  716.        (let ((list nil))
  717.          (if label-obarray
  718.          (mapatoms (function
  719.                 (lambda (sym)
  720.                   (setq list (cons sym list))))
  721.                label-obarray))
  722.          (if list
  723.          (format "BABYL OPTIONS:\nVersion: 5\nLabels: %s\n\037"
  724.              (mapconcat (function symbol-name) list ", "))
  725.            "BABYL OPTIONS:\nVersion: 5\n\037")))
  726.       (t ""))))
  727.  
  728. (defun vm-find-leading-message-separator ()
  729.   "Find the next leading message separator in a folder.
  730. Returns non-nil if the separator is found, nil otherwise."
  731.   (cond
  732.    ((eq vm-folder-type 'From_)
  733.     (let ((reg1 "^From ")
  734.       (reg2 "^>From ")
  735.       (case-fold-search nil))
  736.       (catch 'done
  737.     (while (re-search-forward reg1 nil 'no-error)
  738.       (goto-char (match-beginning 0))
  739.           ;; remove the requirement that there be two
  740.           ;; consecutive newlines (or the beginning of the
  741.           ;; buffer) before "From ".  Hopefully this will not
  742.           ;; break more than it fixes.  (18 August 1995)
  743.       (if ;; (and (or (< (point) 3)
  744.               ;;          (equal (char-after (- (point) 2)) ?\n))
  745.            (save-excursion
  746.              (and (= 0 (forward-line 1))
  747.               (or (vm-match-header)
  748.                   (looking-at reg2))))
  749.           ;; )
  750.           (throw 'done t)
  751.         (forward-char 1)))
  752.     nil )))
  753.    ((eq vm-folder-type 'From_-with-Content-Length)
  754.     (let ((reg1 "\\(^\\|\n+\\)From ")
  755.       (case-fold-search nil))
  756.       (if (re-search-forward reg1 nil 'no-error)
  757.       (progn (goto-char (match-end 1)) t)
  758.     nil )))
  759.    ((eq vm-folder-type 'mmdf)
  760.     (let ((reg1 "^\001\001\001\001")
  761.       (case-fold-search nil))
  762.       (if (re-search-forward reg1 nil 'no-error)
  763.       (progn
  764.         (goto-char (match-beginning 0))
  765.         t )
  766.     nil )))
  767.    ((eq vm-folder-type 'babyl)
  768.     (let ((reg1 "\014\n[01],")
  769.       (case-fold-search nil))
  770.       (catch 'done
  771.     (while (re-search-forward reg1 nil 'no-error)
  772.       (goto-char (match-beginning 0))
  773.       (if (and (not (bobp)) (= (preceding-char) ?\037))
  774.           (throw 'done t)
  775.         (forward-char 1)))
  776.     nil )))))
  777.  
  778. (defun vm-find-trailing-message-separator ()
  779.   "Find the next trailing message separator in a folder."
  780.   (cond
  781.    ((eq vm-folder-type 'From_)
  782.     (vm-find-leading-message-separator)
  783.     (forward-char -1))
  784.    ((eq vm-folder-type 'From_-with-Content-Length)
  785.     (let ((reg1 "^From ")
  786.       content-length
  787.       (start-point (point))
  788.       (case-fold-search nil))
  789.       (if (and (let ((case-fold-search t))
  790.          (re-search-forward vm-content-length-search-regexp nil t))
  791.            (null (match-beginning 1))
  792.            (progn (goto-char (match-beginning 0))
  793.               (vm-match-header vm-content-length-header)))
  794.       (progn
  795.         (setq content-length
  796.           (string-to-int (vm-matched-header-contents)))
  797.         ;; if search fails, we'll be at point-max
  798.         ;; if specified content-length is too long, go to point-max
  799.         (if (search-forward "\n\n" nil 0)
  800.         (if (>= (- (point-max) (point)) content-length)
  801.             (forward-char content-length)
  802.           (goto-char (point-max))))
  803.         ;; Some systems seem to add a trailing newline that's
  804.         ;; not counted in the Content-Length header.  Allow
  805.         ;; any number of them to avoid trouble.
  806.         (skip-chars-forward "\n")))
  807.       (if (or (eobp) (looking-at reg1))
  808.       nil
  809.     (goto-char start-point)
  810.     (if (re-search-forward reg1 nil 0)
  811.         (forward-char -5)))))
  812.    ((eq vm-folder-type 'mmdf)
  813.     (vm-find-leading-message-separator))
  814.    ((eq vm-folder-type 'babyl)
  815.     (vm-find-leading-message-separator)
  816.     (forward-char -1))))
  817.  
  818. (defun vm-skip-past-leading-message-separator ()
  819.   "Move point past a leading message separator at point."
  820.   (cond
  821.    ((memq vm-folder-type '(From_ From_-with-Content-Length))
  822.     (let ((reg1 "^>From ")
  823.       (case-fold-search nil))
  824.       (forward-line 1)
  825.       (while (looking-at reg1)
  826.     (forward-line 1))))
  827.    ((eq vm-folder-type 'mmdf)
  828.     (forward-char 5)
  829.     ;; skip >From.  Either SCO's MMDF implementation leaves this
  830.     ;; stuff in the message, or many sysadmins have screwed up
  831.     ;; their mail configuration.  Either way I'm tired of getting
  832.     ;; bug reports about it.
  833.     (let ((reg1 "^>From ")
  834.       (case-fold-search nil))
  835.       (while (looking-at reg1)
  836.     (forward-line 1))))
  837.    ((eq vm-folder-type 'babyl)
  838.     (search-forward "\n*** EOOH ***\n" nil 0))))
  839.  
  840. (defun vm-skip-past-trailing-message-separator ()
  841.   "Move point past a trailing message separator at point."
  842.   (cond
  843.    ((eq vm-folder-type 'From_)
  844.     (forward-char 1))
  845.    ((eq vm-folder-type 'From_-with-Content-Length))
  846.    ((eq vm-folder-type 'mmdf)
  847.     (forward-char 5))
  848.    ((eq vm-folder-type 'babyl)
  849.     (forward-char 1))))
  850.  
  851. (defun vm-build-message-list ()
  852.   "Build a chain of message structures, stored them in vm-message-list.
  853. Finds the start and end of each message and fills in the relevant
  854. fields in the message structures.
  855.  
  856. Also finds the beginning of the header section and the end of the
  857. text section and fills in these fields in the message structures.
  858.  
  859. vm-text-of and vm-vheaders-of field don't get filled until they
  860. are needed.
  861.  
  862. If vm-message-list already contained messages, the end of the last
  863. known message is found and then the parsing of new messages begins
  864. there and the message are appended to vm-message-list.
  865.  
  866. vm-folder-type is initialized here."
  867.   (setq vm-folder-type (vm-get-folder-type))
  868.   (save-excursion
  869.     (let ((tail-cons nil)
  870.       (n 0)
  871.       ;; Just for yucks, make the update interval vary.
  872.       (modulus (+ (% (vm-abs (random)) 11) 25))
  873.       message last-end)
  874.       (if vm-message-list
  875.       ;; there are already messages, therefore we're supposed
  876.       ;; to add to this list.
  877.       (let ((mp vm-message-list)
  878.         (end (point-min)))
  879.         ;; first we have to find physical end of the folder
  880.         ;; prior to the new messages that just came in.
  881.         (while mp
  882.           (if (< end (vm-end-of (car mp)))
  883.           (setq end (vm-end-of (car mp))))
  884.           (if (not (consp (cdr mp)))
  885.           (setq tail-cons mp))
  886.           (setq mp (cdr mp)))
  887.         (goto-char end))
  888.     ;; there are no messages so we're building the whole list.
  889.     ;; start from the beginning of the folder.
  890.     (goto-char (point-min))
  891.     ;; whine about newlines at the beginning of the folder.
  892.     ;; technically I think this is corruption, but there are
  893.     ;; too many busted mail-do-fcc's installed out there to
  894.     ;; do more than whine.
  895.     (if (and (memq vm-folder-type '(From_ From_-with-Content-Length))
  896.          (= (following-char) ?\n))
  897.         (progn
  898.           (message "Warning: newline found at beginning of folder, %s"
  899.                (or buffer-file-name (buffer-name)))
  900.           (sleep-for 2)))
  901.     (vm-skip-past-folder-header))
  902.       (setq last-end (point))
  903.       ;; parse the messages, set the markers that specify where
  904.       ;; things are.
  905.       (while (vm-find-leading-message-separator)
  906.     (setq message (vm-make-message))
  907.     (vm-set-message-type-of message vm-folder-type)
  908.     (vm-set-start-of message (vm-marker (point)))
  909.     (vm-skip-past-leading-message-separator)
  910.     (vm-set-headers-of message (vm-marker (point)))
  911.     (vm-find-trailing-message-separator)
  912.     (vm-set-text-end-of message (vm-marker (point)))
  913.     (vm-skip-past-trailing-message-separator)
  914.     (setq last-end (point))
  915.     (vm-set-end-of message (vm-marker (point)))
  916.     (vm-set-reverse-link-of message tail-cons)
  917.     (if (null tail-cons)
  918.         (setq vm-message-list (list message)
  919.           tail-cons vm-message-list)
  920.       (setcdr tail-cons (list message))
  921.       (setq tail-cons (cdr tail-cons)))
  922.     (vm-increment n)
  923.     (if (zerop (% n modulus))
  924.         (message "Parsing messages... %d" n)))
  925.       (if (>= n modulus)
  926.       (message "Parsing messages... done"))
  927.       (if (and (not (= last-end (point-max)))
  928.            (not (eq vm-folder-type 'unknown)))
  929.       (progn
  930.         (message "Warning: garbage found at end of folder, %s"
  931.              (or buffer-file-name (buffer-name)))
  932.         (sleep-for 2))))))
  933.  
  934. (defun vm-build-header-order-alist (vheaders)
  935.   (let ((order-alist (cons nil nil))
  936.     list)
  937.     (setq list order-alist)
  938.     (while vheaders
  939.       (setcdr list (cons (cons (car vheaders) nil) nil))
  940.       (setq list (cdr list) vheaders (cdr vheaders)))
  941.     (cdr order-alist)))
  942.  
  943. ;; Reorder the headers in a message.
  944. ;;
  945. ;; If a message struct is passed into this function, then we're
  946. ;; operating on a message in a folder buffer.  Headers are
  947. ;; grouped so that the headers that the user wants to see are at
  948. ;; the end of the headers section so we can narrow to them.  This
  949. ;; is done according to the preferences specified in
  950. ;; vm-visible-header and vm-invisible-header-regexp.  The
  951. ;; vheaders field of the message struct is also set.  This
  952. ;; function is called on demand whenever a vheaders field is
  953. ;; discovered to be nil for a particular message.
  954. ;;
  955. ;; If the message argument is nil, then we are operating on a
  956. ;; freestanding message that is not part of a folder buffer.  The
  957. ;; keep-list and discard-regexp parameters are used in this case.
  958. ;; Headers not matched by the keep list or matched by the discard
  959. ;; list are stripped from the message.  The remaining headers
  960. ;; are ordered according to the order of the keep list.
  961.  
  962. (defun vm-reorder-message-headers (message keep-list discard-regexp)
  963.   (save-excursion
  964.     (if message
  965.     (progn
  966.       (set-buffer (vm-buffer-of message))
  967.       (setq keep-list vm-visible-headers
  968.         discard-regexp vm-invisible-header-regexp)))
  969.     (save-excursion
  970.       (save-restriction
  971.     (widen)
  972.     ;; if there is a cached regexp that points to the already
  973.     ;; ordered headers then use it and avoid a lot of work.
  974.     (if (and message (vm-vheaders-regexp-of message))
  975.         (save-excursion
  976.           (goto-char (vm-headers-of message))
  977.           (let ((case-fold-search t))
  978.         (re-search-forward (vm-vheaders-regexp-of message)
  979.                    (vm-text-of message) t))
  980.           (vm-set-vheaders-of message (vm-marker (match-beginning 0))))
  981.       ;; oh well, we gotta do it the hard way.
  982.       ;;
  983.       ;; header-alist will contain an assoc list version of
  984.       ;; keep-list.  For messages associated with a folder
  985.       ;; buffer: when a matching header is found, the
  986.       ;; header's start and end positions are added to its
  987.       ;; corresponding assoc cell.  The positions of unwanted
  988.       ;; headers are remember also so that they can be copied
  989.       ;; to the top of the message, to be out of sight after
  990.       ;; narrowing.  Once the positions have all been
  991.       ;; recorded a new copy of the headers is inserted in
  992.       ;; the proper order and the old headers are deleted.
  993.       ;;
  994.       ;; For free standing messages, unwanted headers are
  995.       ;; stripped from the message, unremembered.
  996.       (vm-save-restriction
  997.        (let ((header-alist (vm-build-header-order-alist keep-list))
  998.          (buffer-read-only nil)
  999.          (work-buffer nil)
  1000.          (extras nil)
  1001.          list end-of-header vheader-offset
  1002.          (folder-buffer (current-buffer))
  1003.          ;; This prevents file locking from occuring.  Disabling
  1004.          ;; locking can speed things noticeably if the lock directory
  1005.          ;; is on a slow device.  We don't need locking here because
  1006.          ;; in a mail context reordering headers is harmless.
  1007.          (buffer-file-name nil)
  1008.          (case-fold-search t)
  1009.          (unwanted-list nil)
  1010.          unwanted-tail
  1011.          new-header-start
  1012.          old-header-start
  1013.          (old-buffer-modified-p (buffer-modified-p)))
  1014.          (unwind-protect
  1015.          (progn
  1016.            (if message
  1017.                (progn
  1018.              ;; for babyl folders, keep an untouched
  1019.              ;; copy of the headers between the
  1020.              ;; attributes line and the *** EOOH ***
  1021.              ;; line.
  1022.              (if (and (eq vm-folder-type 'babyl)
  1023.                   (null (vm-babyl-frob-flag-of message)))
  1024.                  (progn
  1025.                    (goto-char (vm-start-of message))
  1026.                    (forward-line 2)
  1027.                    (vm-set-babyl-frob-flag-of message t)
  1028.                    (insert-buffer-substring
  1029.                 (current-buffer)
  1030.                 (vm-headers-of message)
  1031.                 (1- (vm-text-of message)))))
  1032.              (setq work-buffer (generate-new-buffer "*vm-work*"))
  1033.              (set-buffer work-buffer)
  1034.              (insert-buffer-substring
  1035.               folder-buffer 
  1036.               (vm-headers-of message)
  1037.               (vm-text-of message))
  1038.              (goto-char (point-min))))
  1039.            (setq old-header-start (point))
  1040.            ;; as we loop through the headers, skip >From
  1041.            ;; lines.  these can occur anywhere in the
  1042.            ;; header section if the message has been
  1043.            ;; manhandled by some dumb delivery agents
  1044.            ;; (SCO and Solaris are the usual suspects.)
  1045.            ;; it's a tough ol' world.
  1046.            (while (progn (while (looking-at ">From ")
  1047.                    (forward-line))
  1048.                  (and (not (= (following-char) ?\n))
  1049.                       (vm-match-header)))
  1050.              (setq end-of-header (vm-matched-header-end)
  1051.                list (vm-match-ordered-header header-alist))
  1052.              ;; don't display/keep this header if
  1053.              ;;  keep-list not matched
  1054.              ;;  and discard-regexp is nil
  1055.              ;;       or
  1056.              ;;  discard-regexp is matched
  1057.              (if (or (and (null list) (null discard-regexp))
  1058.                  (and discard-regexp (looking-at discard-regexp)))
  1059.              ;; delete the unwanted header if not doing
  1060.              ;; work for a folder buffer, otherwise
  1061.              ;; remember the start and end of the
  1062.              ;; unwanted header so we can copy it
  1063.              ;; later.
  1064.              (if (not message)
  1065.                  (delete-region (point) end-of-header)
  1066.                (if (null unwanted-list)
  1067.                    (setq unwanted-list
  1068.                      (cons (point) (cons end-of-header nil))
  1069.                      unwanted-tail unwanted-list)
  1070.                  (if (= (point) (car (cdr unwanted-tail)))
  1071.                  (setcar (cdr unwanted-tail)
  1072.                      end-of-header)
  1073.                    (setcdr (cdr unwanted-tail)
  1074.                        (cons (point)
  1075.                          (cons end-of-header nil)))
  1076.                    (setq unwanted-tail (cdr (cdr unwanted-tail)))))
  1077.                (goto-char end-of-header))
  1078.                ;; got a match
  1079.                ;; stuff the start and end of the header
  1080.                ;; into the cdr of the returned alist
  1081.                ;; element.
  1082.                (if list
  1083.                ;; reverse point and end-of-header.
  1084.                ;; list will be nreversed later.
  1085.                (setcdr list (cons end-of-header
  1086.                           (cons (point)
  1087.                             (cdr list))))
  1088.              ;; reverse point and end-of-header.
  1089.              ;; list will be nreversed later.
  1090.              (setq extras
  1091.                    (cons end-of-header
  1092.                      (cons (point) extras))))
  1093.                (goto-char end-of-header)))
  1094.            (setq new-header-start (point))
  1095.            (while unwanted-list
  1096.              (insert-buffer-substring (current-buffer)
  1097.                           (car unwanted-list)
  1098.                           (car (cdr unwanted-list)))
  1099.              (setq unwanted-list (cdr (cdr unwanted-list))))
  1100.            ;; remember the offset of where the visible
  1101.            ;; header start so we can initialize the
  1102.            ;; vm-vheaders-of field later.
  1103.            (if message
  1104.                (setq vheader-offset (- (point) new-header-start)))
  1105.            (while header-alist
  1106.              (setq list (nreverse (cdr (car header-alist))))
  1107.              (while list
  1108.                (insert-buffer-substring (current-buffer)
  1109.                         (car list)
  1110.                         (car (cdr list)))
  1111.                (setq list (cdr (cdr list))))
  1112.              (setq header-alist (cdr header-alist)))
  1113.            ;; now the headers that were not explicitly
  1114.            ;; undesirable, if any.
  1115.            (setq extras (nreverse extras))
  1116.            (while extras
  1117.              (insert-buffer-substring (current-buffer)
  1118.                           (car extras)
  1119.                           (car (cdr extras)))
  1120.              (setq extras (cdr (cdr extras))))
  1121.            (delete-region old-header-start new-header-start)
  1122.            ;; update the folder buffer if we're supposed to.
  1123.            ;; lock out interrupts.
  1124.            (if message
  1125.                (let ((inhibit-quit t))
  1126.              (set-buffer (vm-buffer-of message))
  1127.              (goto-char (vm-headers-of message))
  1128.              (insert-buffer-substring work-buffer)
  1129.              (delete-region (point) (vm-text-of message))
  1130.              (set-buffer-modified-p old-buffer-modified-p))))
  1131.            (and work-buffer (kill-buffer work-buffer)))
  1132.          (if message
  1133.          (progn
  1134.            (vm-set-vheaders-of message
  1135.                        (vm-marker (+ (vm-headers-of message)
  1136.                              vheader-offset)))
  1137.            ;; cache a regular expression that can be used to
  1138.            ;; find the start of the reordered header the next
  1139.            ;; time this folder is visited.
  1140.            (goto-char (vm-vheaders-of message))
  1141.            (if (vm-match-header)
  1142.                (vm-set-vheaders-regexp-of
  1143.             message
  1144.             (concat "^" (vm-matched-header-name) ":"))))))))))))
  1145.  
  1146. ;; Reads the message attributes and cached header information from the
  1147. ;; header portion of the each message, if our X-VM- attributes header is
  1148. ;; present.  If the header is not present, assume the message is new,
  1149. ;; unless we are being compatible with Berkeley Mail in which case we
  1150. ;; also check for a Status header.
  1151. ;;
  1152. ;; If a message already has attributes don't bother checking the
  1153. ;; headers.
  1154. ;;
  1155. ;; This function also discovers and stores the position where the
  1156. ;; message text begins.
  1157. ;;
  1158. ;; Totals are gathered for use by vm-emit-totals-blurb.
  1159. ;;
  1160. ;; Supports version 4 format of attribute storage, for backward compatibility.
  1161.  
  1162. (defun vm-read-attributes (message-list)
  1163.   (save-excursion
  1164.     (let ((mp (or message-list vm-message-list))
  1165.       (vm-new-count 0)
  1166.       (vm-unread-count 0)
  1167.       (vm-deleted-count 0)
  1168.       (vm-total-count 0)
  1169.       (modulus (+ (% (vm-abs (random)) 11) 25))
  1170.       (case-fold-search t)
  1171.       oldpoint data)
  1172.       (while mp
  1173.     (vm-increment vm-total-count)
  1174.     (if (vm-attributes-of (car mp))
  1175.         ()
  1176.       (goto-char (vm-headers-of (car mp)))
  1177.       ;; find start of text section and save it
  1178.       (search-forward "\n\n" (vm-text-end-of (car mp)) 0)
  1179.       (vm-set-text-of (car mp) (point-marker))
  1180.       ;; now look for our header
  1181.       (goto-char (vm-headers-of (car mp)))
  1182.       (cond
  1183.        ((re-search-forward vm-attributes-header-regexp
  1184.                    (vm-text-of (car mp)) t)
  1185.         (goto-char (match-beginning 2))
  1186.         (condition-case ()
  1187.         (progn
  1188.           (setq oldpoint (point)
  1189.             data (read (current-buffer)))
  1190.           (if (and (or (not (listp data)) (not (> (length data) 1)))
  1191.                (not (vectorp data)))
  1192.               (progn
  1193.             (error "Bad x-vm-v5-data at %d in buffer %s"
  1194.                    oldpoint (buffer-name))))
  1195.           data )
  1196.           (error 
  1197.            (message "Bad x-vm-v5-data header at %d in buffer %s, ignoring"
  1198.             oldpoint (buffer-name))
  1199.            (setq data
  1200.              (list
  1201.               (make-vector vm-attributes-vector-length nil)
  1202.               (make-vector vm-cache-vector-length nil)
  1203.               nil))
  1204.            ;; In lieu of a valid attributes header
  1205.            ;; assume the message is new.  avoid
  1206.            ;; vm-set-new-flag because it asks for a
  1207.            ;; summary update.
  1208.            (vm-set-new-flag-in-vector (car data) t)))
  1209.         ;; support version 4 format
  1210.         (cond ((vectorp data)
  1211.            (setq data (vm-convert-v4-attributes data))
  1212.            ;; tink the message modflag so that if the
  1213.            ;; user saves we get rid of the old v4
  1214.            ;; attributes header.  otherwise we could be
  1215.            ;; dealing with these things for all eternity.
  1216.            (vm-set-modflag-of (car mp) t))
  1217.           (t
  1218.            ;; extend vectors if necessary to accomodate
  1219.            ;; more caching and attributes without alienating
  1220.            ;; other version 5 folders.
  1221.            (cond ((< (length (car data))
  1222.                  vm-attributes-vector-length)
  1223.               ;; tink the message modflag so that if
  1224.               ;; the user saves we get rid of the old
  1225.               ;; short vector.  otherwise we could be
  1226.               ;; dealing with these things for all
  1227.               ;; eternity.
  1228.               (vm-set-modflag-of (car mp) t)
  1229.               (setcar data (vm-extend-vector
  1230.                     (car data)
  1231.                     vm-attributes-vector-length))))
  1232.            (cond ((< (length (car (cdr data)))
  1233.                  vm-cache-vector-length)
  1234.               ;; tink the message modflag so that if
  1235.               ;; the user saves we get rid of the old
  1236.               ;; short vector.  otherwise we could be
  1237.               ;; dealing with these things for all
  1238.               ;; eternity.
  1239.               (vm-set-modflag-of (car mp) t)
  1240.               (setcar (cdr data)
  1241.                   (vm-extend-vector
  1242.                    (car (cdr data))
  1243.                    vm-cache-vector-length))))))
  1244.         ;; data list might not be long enough for (nth 2 ...)  but
  1245.         ;; that's OK because nth returns nil if you overshoot the
  1246.         ;; end of the list.
  1247.         (vm-set-labels-of (car mp) (nth 2 data))
  1248.         (vm-set-cache-of (car mp) (car (cdr data)))
  1249.         (vm-set-attributes-of (car mp) (car data)))
  1250.        ((and vm-berkeley-mail-compatibility
  1251.          (re-search-forward vm-berkeley-mail-status-header-regexp
  1252.                     (vm-text-of (car mp)) t))
  1253.         (vm-set-cache-of (car mp) (make-vector vm-cache-vector-length
  1254.                            nil))
  1255.         (goto-char (match-beginning 1))
  1256.         (vm-set-attributes-of
  1257.          (car mp)
  1258.          (make-vector vm-attributes-vector-length nil))
  1259.         (vm-set-unread-flag (car mp) (not (looking-at ".*R.*")) t))
  1260.        (t
  1261.         (vm-set-cache-of (car mp) (make-vector vm-cache-vector-length
  1262.                            nil))
  1263.         (vm-set-attributes-of
  1264.          (car mp)
  1265.          (make-vector vm-attributes-vector-length nil))
  1266.         ;; In lieu of a valid attributes header
  1267.         ;; assume the message is new.  avoid
  1268.         ;; vm-set-new-flag because it asks for a
  1269.         ;; summary update.
  1270.         (vm-set-new-flag-of (car mp) t)))
  1271.       ;; let babyl attributes override the normal VM
  1272.       ;; attributes header.
  1273.       (cond ((eq vm-folder-type 'babyl)
  1274.          (vm-read-babyl-attributes (car mp)))))
  1275.     (cond ((vm-deleted-flag (car mp))
  1276.            (vm-increment vm-deleted-count))
  1277.           ((vm-new-flag (car mp))
  1278.            (vm-increment vm-new-count))
  1279.           ((vm-unread-flag (car mp))
  1280.            (vm-increment vm-unread-count)))
  1281.     (if (zerop (% vm-total-count modulus))
  1282.         (message "Reading attributes... %d" vm-total-count))
  1283.     (setq mp (cdr mp)))
  1284.       (if (>= vm-total-count modulus)
  1285.       (message "Reading attributes... done"))
  1286.       (if (null message-list)
  1287.       (setq vm-totals (list vm-modification-counter
  1288.                 vm-total-count
  1289.                 vm-new-count
  1290.                 vm-unread-count
  1291.                 vm-deleted-count))))))
  1292.  
  1293. (defun vm-read-babyl-attributes (message)
  1294.   (let ((case-fold-search t)
  1295.     (labels nil)
  1296.     (vect (make-vector vm-attributes-vector-length nil)))
  1297.     (vm-set-attributes-of message vect)
  1298.     (save-excursion
  1299.       (goto-char (vm-start-of message))
  1300.       ;; skip past ^L\n
  1301.       (forward-char 2)
  1302.       (vm-set-babyl-frob-flag-of message (if (= (following-char) ?1) t nil))
  1303.       ;; skip past 0,
  1304.       (forward-char 2)
  1305.       ;; loop, noting attributes as we go.
  1306.       (while (and (not (eobp)) (not (looking-at ",")))
  1307.     (cond ((looking-at " unseen,")
  1308.            (vm-set-unread-flag-of message t))
  1309.           ((looking-at " recent,")
  1310.            (vm-set-new-flag-of message t))
  1311.           ((looking-at " deleted,")
  1312.            (vm-set-deleted-flag-of message t))
  1313.           ((looking-at " answered,")
  1314.            (vm-set-replied-flag-of message t))
  1315.           ((looking-at " forwarded,")
  1316.            (vm-set-forwarded-flag-of message t))
  1317.           ((looking-at " filed,")
  1318.            (vm-set-filed-flag-of message t))
  1319.           ((looking-at " redistributed,")
  1320.            (vm-set-redistributed-flag-of message t))
  1321.           ;; only VM knows about these, as far as I know.
  1322.           ((looking-at " edited,")
  1323.            (vm-set-forwarded-flag-of message t))
  1324.           ((looking-at " written,")
  1325.            (vm-set-forwarded-flag-of message t)))
  1326.     (skip-chars-forward "^,")
  1327.     (and (not (eobp)) (forward-char 1)))
  1328.       (and (not (eobp)) (forward-char 1))
  1329.       (while (looking-at " \\([^\000-\040,\177-\377]+\\),")
  1330.     (setq labels (cons (vm-buffer-substring-no-properties
  1331.                 (match-beginning 1)
  1332.                 (match-end 1))
  1333.                labels))
  1334.     (goto-char (match-end 0)))
  1335.       (vm-set-labels-of message labels))))
  1336.  
  1337. (defun vm-set-default-attributes (message-list)
  1338.   (let ((mp (or message-list vm-message-list)) attr cache)
  1339.     (while mp
  1340.       (setq attr (make-vector vm-attributes-vector-length nil)
  1341.         cache (make-vector vm-cache-vector-length nil))
  1342.       (vm-set-cache-of (car mp) cache)
  1343.       (vm-set-attributes-of (car mp) attr)
  1344.       ;; make message be new by default, but avoid vm-set-new-flag
  1345.       ;; because it asks for a summary update for the message.
  1346.       (vm-set-new-flag-of (car mp) t)
  1347.       ;; since this function is usually called in lieu of reading
  1348.       ;; attributes from the buffer, the attributes may be
  1349.       ;; untrustworthy.  tink the message modflag to force the
  1350.       ;; new attributes out if the user saves.
  1351.       (vm-set-modflag-of (car mp) t)
  1352.       (setq mp (cdr mp)))))
  1353.  
  1354. (defun vm-emit-totals-blurb ()
  1355.   (save-excursion
  1356.     (vm-select-folder-buffer)
  1357.     (if (not (equal (nth 0 vm-totals) vm-modification-counter))
  1358.     (let ((mp vm-message-list)
  1359.           (vm-new-count 0)
  1360.           (vm-unread-count 0)
  1361.           (vm-deleted-count 0)
  1362.           (vm-total-count 0))
  1363.       (while mp
  1364.         (vm-increment vm-total-count)
  1365.         (cond ((vm-deleted-flag (car mp))
  1366.            (vm-increment vm-deleted-count))
  1367.           ((vm-new-flag (car mp))
  1368.            (vm-increment vm-new-count))
  1369.           ((vm-unread-flag (car mp))
  1370.            (vm-increment vm-unread-count)))
  1371.         (setq mp (cdr mp)))
  1372.       (setq vm-totals (list vm-modification-counter
  1373.                 vm-total-count
  1374.                 vm-new-count
  1375.                 vm-unread-count
  1376.                 vm-deleted-count))))
  1377.     (if (equal (nth 1 vm-totals) 0)
  1378.     (message "No messages.")
  1379.       (message "%d message%s, %d new, %d unread, %d deleted"
  1380.            (nth 1 vm-totals) (if (= (nth 1 vm-totals) 1) "" "s")
  1381.            (nth 2 vm-totals)
  1382.            (nth 3 vm-totals)
  1383.            (nth 4 vm-totals)))))
  1384.  
  1385. (defun vm-convert-v4-attributes (data)
  1386.   (list (apply 'vector
  1387.            (nconc (vm-vector-to-list data)
  1388.               (make-list (- vm-attributes-vector-length
  1389.                     (length data))
  1390.                  nil)))
  1391.     (make-vector vm-cache-vector-length nil)))
  1392.  
  1393. (defun vm-gobble-last-modified ()
  1394.   (let ((case-fold-search t)
  1395.     time lim oldpoint)
  1396.     (save-excursion
  1397.       (vm-save-restriction
  1398.        (widen)
  1399.        (goto-char (point-min))
  1400.        (vm-skip-past-folder-header)
  1401.        (vm-skip-past-leading-message-separator)
  1402.        (search-forward "\n\n" nil t)
  1403.        (setq lim (point))
  1404.        (goto-char (point-min))
  1405.        (vm-skip-past-folder-header)
  1406.        (vm-skip-past-leading-message-separator)
  1407.        (if (re-search-forward vm-last-modified-header-regexp lim t)
  1408.        (condition-case ()
  1409.            (progn
  1410.          (setq oldpoint (point)
  1411.                time (read (current-buffer)))
  1412.          (if (not (consp time))
  1413.              (error "Bad last-modified header at %d in buffer %s"
  1414.                 oldpoint (buffer-name)))
  1415.          time )
  1416.          (error
  1417.           (message "Bad last-modified header at %d in buffer %s, ignoring"
  1418.                oldpoint (buffer-name))
  1419.           (setq time '(0 0 0)))))))
  1420.     time ))
  1421.  
  1422. (defun vm-gobble-labels ()
  1423.   (let ((case-fold-search t)
  1424.     lim)
  1425.     (save-excursion
  1426.       (vm-save-restriction
  1427.        (widen)
  1428.        (if (eq vm-folder-type 'babyl)
  1429.        (progn
  1430.          (goto-char (point-min))
  1431.          (vm-skip-past-folder-header)
  1432.          (setq lim (point))
  1433.          (goto-char (point-min))
  1434.          (if (re-search-forward "^Labels:" lim t)
  1435.          (let (string list)
  1436.            (setq string (buffer-substring
  1437.                  (point)
  1438.                  (progn (end-of-line) (point)))
  1439.              list (vm-parse string
  1440. "[\000-\040,\177-\377]*\\([^\000-\040,\177-\377]+\\)[\000-\040,\177-\377]*"))
  1441.            (mapcar (function
  1442.                 (lambda (s)
  1443.                   (intern (downcase s) vm-label-obarray)))
  1444.                list))))
  1445.      (goto-char (point-min))
  1446.      (vm-skip-past-folder-header)
  1447.      (vm-skip-past-leading-message-separator)
  1448.      (search-forward "\n\n" nil t)
  1449.      (setq lim (point))
  1450.      (goto-char (point-min))
  1451.      (vm-skip-past-folder-header)
  1452.      (vm-skip-past-leading-message-separator)
  1453.      (if (re-search-forward vm-labels-header-regexp lim t)
  1454.          (let ((oldpoint (point))
  1455.            list)
  1456.            (condition-case ()
  1457.            (progn
  1458.              (setq list (read (current-buffer)))
  1459.              (if (not (listp list))
  1460.              (error "Bad global label list at %d in buffer %s"
  1461.                 oldpoint (buffer-name)))
  1462.              list )
  1463.          (error
  1464.           (message "Bad global label list at %d in buffer %s, ignoring"
  1465.                oldpoint (buffer-name))
  1466.           (setq list nil) ))
  1467.            (vm-startup-apply-labels list))))))
  1468.     t ))
  1469.  
  1470. (defun vm-startup-apply-labels (labels)
  1471.   (mapcar (function (lambda (s) (intern s vm-label-obarray))) labels))
  1472.  
  1473. ;; Go to the message specified in a bookmark and eat the bookmark.
  1474. ;; Returns non-nil if successful, nil otherwise.
  1475. (defun vm-gobble-bookmark ()
  1476.   (let ((case-fold-search t)
  1477.     (n nil)
  1478.     lim oldpoint)
  1479.     (save-excursion
  1480.       (vm-save-restriction
  1481.        (widen)
  1482.        (goto-char (point-min))
  1483.        (vm-skip-past-folder-header)
  1484.        (vm-skip-past-leading-message-separator)
  1485.        (search-forward "\n\n" nil t)
  1486.        (setq lim (point))
  1487.        (goto-char (point-min))
  1488.        (vm-skip-past-folder-header)
  1489.        (vm-skip-past-leading-message-separator)
  1490.        (if (re-search-forward vm-bookmark-header-regexp lim t)
  1491.        (condition-case ()
  1492.            (progn
  1493.          (setq oldpoint (point)
  1494.                n (read (current-buffer)))
  1495.          (if (not (natnump n))
  1496.              (error "Bad bookmark at %d in buffer %s"
  1497.                 oldpoint (buffer-name)))
  1498.          n )
  1499.          (error
  1500.           (message "Bad bookmark at %d in buffer %s, ignoring"
  1501.                oldpoint (buffer-name))
  1502.           (setq n 1))))))
  1503.     (vm-startup-apply-bookmark n)
  1504.     t ))
  1505.  
  1506. (defun vm-startup-apply-bookmark (n)
  1507.   (if n
  1508.       (vm-record-and-change-message-pointer
  1509.        vm-message-pointer
  1510.        (nthcdr (1- n) vm-message-list))))
  1511.  
  1512. (defun vm-gobble-pop-retrieved ()
  1513.   (let ((case-fold-search t)
  1514.     ob lim oldpoint)
  1515.     (save-excursion
  1516.       (vm-save-restriction
  1517.        (widen)
  1518.        (goto-char (point-min))
  1519.        (vm-skip-past-folder-header)
  1520.        (vm-skip-past-leading-message-separator)
  1521.        (search-forward "\n\n" nil t)
  1522.        (setq lim (point))
  1523.        (goto-char (point-min))
  1524.        (vm-skip-past-folder-header)
  1525.        (vm-skip-past-leading-message-separator)
  1526.        (if (re-search-forward vm-pop-retrieved-header-regexp lim t)
  1527.        (condition-case ()
  1528.            (progn
  1529.          (setq oldpoint (point)
  1530.                ob (read (current-buffer)))
  1531.          (if (not (listp ob))
  1532.              (error "Bad pop-retrieved header at %d in buffer %s"
  1533.                 oldpoint (buffer-name)))
  1534.          (setq vm-pop-retrieved-messages ob))
  1535.          (error
  1536.           (message "Bad pop-retrieved header at %d in buffer %s, ignoring"
  1537.                oldpoint (buffer-name)))))))
  1538.     t ))
  1539.  
  1540. (defun vm-gobble-visible-header-variables ()
  1541.   (save-excursion
  1542.     (vm-save-restriction
  1543.      (let ((case-fold-search t)
  1544.        lim)
  1545.        (widen)
  1546.        (goto-char (point-min))
  1547.        (vm-skip-past-folder-header)
  1548.        (vm-skip-past-leading-message-separator)
  1549.        (search-forward "\n\n" nil t)
  1550.        (setq lim (point))
  1551.        (goto-char (point-min))
  1552.        (vm-skip-past-folder-header)
  1553.        (vm-skip-past-leading-message-separator)
  1554.        (if (re-search-forward vm-vheader-header-regexp lim t)
  1555.        (let (vis invis (got nil))
  1556.          (condition-case ()
  1557.          (setq vis (read (current-buffer))
  1558.                invis (read (current-buffer))
  1559.                got t)
  1560.            (error nil))
  1561.          (if got
  1562.          (vm-startup-apply-header-variables vis invis))))))))
  1563.  
  1564. (defun vm-startup-apply-header-variables (vis invis)
  1565.   ;; if the variables don't match the values stored when this
  1566.   ;; folder was saved, then we have to discard any cached
  1567.   ;; vheader info so the user will see the right headers.
  1568.   (and (or (not (equal vis vm-visible-headers))
  1569.        (not (equal invis vm-invisible-header-regexp)))
  1570.        (let ((mp vm-message-list))
  1571.      (message "Discarding visible header info...")
  1572.      (while mp
  1573.        (vm-set-vheaders-regexp-of (car mp) nil)
  1574.        (vm-set-vheaders-of (car mp) nil)
  1575.        (setq mp (cdr mp))))))
  1576.  
  1577. ;; Read and delete the header that gives the folder's desired
  1578. ;; message order.
  1579. (defun vm-gobble-message-order ()
  1580.   (let ((case-fold-search t)
  1581.     lim order)
  1582.     (save-excursion
  1583.       (save-restriction
  1584.     (widen)
  1585.     (goto-char (point-min))
  1586.     (vm-skip-past-folder-header)
  1587.     (vm-skip-past-leading-message-separator)
  1588.     (search-forward "\n\n" nil t)
  1589.     (setq lim (point))
  1590.     (goto-char (point-min))
  1591.     (vm-skip-past-folder-header)
  1592.     (vm-skip-past-leading-message-separator)
  1593.     (if (re-search-forward vm-message-order-header-regexp lim t)
  1594.         (let ((oldpoint (point)))
  1595.           (condition-case nil
  1596.           (progn
  1597.             (setq order (read (current-buffer)))
  1598.             (if (not (listp order))
  1599.             (error "Bad order header at %d in buffer %s"
  1600.                    oldpoint (buffer-name)))
  1601.             order )
  1602.         (error
  1603.          (message "Bad order header at %d in buffer %s, ignoring"
  1604.               oldpoint (buffer-name))
  1605.          (setq order nil)))
  1606.           (if order
  1607.           (progn
  1608.             (message "Reordering messages...")
  1609.             (vm-startup-apply-message-order order)
  1610.             (message "Reordering messages... done")))))))))
  1611.  
  1612. (defun vm-startup-apply-message-order (order)
  1613.   (let (list-length v (mp vm-message-list))
  1614.     (setq list-length (length vm-message-list)
  1615.       v (make-vector (max list-length (length order)) nil))
  1616.     (while (and order mp)
  1617.       (condition-case nil
  1618.       (aset v (1- (car order)) (car mp))
  1619.     (args-out-of-range nil))
  1620.       (setq order (cdr order) mp (cdr mp)))
  1621.     ;; lock out interrupts while the message list is in
  1622.     ;; an inconsistent state.
  1623.     (let ((inhibit-quit t))
  1624.       (setq vm-message-list (delq nil (append v mp))
  1625.         vm-message-order-changed nil
  1626.         vm-message-order-header-present t
  1627.         vm-message-pointer (memq (car vm-message-pointer)
  1628.                      vm-message-list))
  1629.       (vm-set-numbering-redo-start-point t)
  1630.       (vm-reverse-link-messages))))
  1631.  
  1632. ;; Read the header that gives the folder's cached summary format
  1633. ;; If the current summary format is different, then the cached
  1634. ;; summary lines are discarded.
  1635. (defun vm-gobble-summary ()
  1636.   (let ((case-fold-search t)
  1637.     summary lim)
  1638.     (save-excursion
  1639.       (vm-save-restriction
  1640.        (widen)
  1641.        (goto-char (point-min))
  1642.        (vm-skip-past-folder-header)
  1643.        (vm-skip-past-leading-message-separator)
  1644.        (search-forward "\n\n" nil t)
  1645.        (setq lim (point))
  1646.        (goto-char (point-min))
  1647.        (vm-skip-past-folder-header)
  1648.        (vm-skip-past-leading-message-separator)
  1649.        (if (re-search-forward vm-summary-header-regexp lim t)
  1650.        (let ((oldpoint (point)))
  1651.          (condition-case ()
  1652.          (setq summary (read (current-buffer)))
  1653.            (error
  1654.         (message "Bad summary header at %d in buffer %s, ignoring"
  1655.              oldpoint (buffer-name))
  1656.         (setq summary "")))
  1657.          (vm-startup-apply-summary summary)))))))
  1658.  
  1659. (defun vm-startup-apply-summary (summary)
  1660.   (if (not (equal summary vm-summary-format))
  1661.       (let ((mp vm-message-list))
  1662.     (while mp
  1663.       (vm-set-summary-of (car mp) nil)
  1664.       ;; force restuffing of cache to clear old
  1665.       ;; summary entry cache.
  1666.       (vm-set-modflag-of (car mp) t)
  1667.       (setq mp (cdr mp))))))
  1668.  
  1669. ;; Stuff the message attributes back into the message as headers.
  1670. (defun vm-stuff-attributes (m &optional for-other-folder)
  1671.   (save-excursion
  1672.     (vm-save-restriction
  1673.      (widen)
  1674.      (let ((old-buffer-modified-p (buffer-modified-p))
  1675.        attributes cache
  1676.        (case-fold-search t)
  1677.        (buffer-read-only nil)
  1678.         ;; don't truncate the printing of large Lisp objects
  1679.         (print-length nil)
  1680.        opoint
  1681.        ;; This prevents file locking from occuring.  Disabling
  1682.        ;; locking can speed things noticeably if the lock
  1683.        ;; directory is on a slow device.  We don't need locking
  1684.        ;; here because the user shouldn't care about VM stuffing
  1685.        ;; its own status headers.
  1686.        (buffer-file-name nil)
  1687.        (delflag (vm-deleted-flag m)))
  1688.        (unwind-protect
  1689.        (progn
  1690.          ;; don't put this folder's summary entry into another folder.
  1691.          (if for-other-folder
  1692.          (vm-set-summary-of m nil)
  1693.            (if (vm-su-start-of m)
  1694.            ;; fill the summary cache if it's not done already.
  1695.            (vm-su-summary m)))
  1696.          (setq attributes (vm-attributes-of m)
  1697.            cache (vm-cache-of m))
  1698.          (and delflag for-other-folder
  1699.           (vm-set-deleted-flag-in-vector
  1700.            (setq attributes (copy-sequence attributes)) nil))
  1701.          (if (eq vm-folder-type 'babyl)
  1702.          (vm-stuff-babyl-attributes m for-other-folder))
  1703.          (goto-char (vm-headers-of m))
  1704.          (while (re-search-forward vm-attributes-header-regexp
  1705.                        (vm-text-of m) t)
  1706.            (delete-region (match-beginning 0) (match-end 0)))
  1707.          (goto-char (vm-headers-of m))
  1708.          (setq opoint (point))
  1709.          (insert-before-markers
  1710.           vm-attributes-header " ("
  1711.           (let ((print-escape-newlines t))
  1712.         (prin1-to-string attributes))
  1713.           "\n\t"
  1714.           (let ((print-escape-newlines t))
  1715.         (prin1-to-string cache))
  1716.           "\n\t"
  1717.           (let ((print-escape-newlines t))
  1718.         (prin1-to-string (vm-labels-of m)))
  1719.           ")\n")
  1720.          (set-marker (vm-headers-of m) opoint)
  1721.          (cond ((and (eq vm-folder-type 'From_)
  1722.              vm-berkeley-mail-compatibility)
  1723.             (goto-char (vm-headers-of m))
  1724.             (while (re-search-forward
  1725.                 vm-berkeley-mail-status-header-regexp
  1726.                 (vm-text-of m) t)
  1727.               (delete-region (match-beginning 0) (match-end 0)))
  1728.             (goto-char (vm-headers-of m))
  1729.             (cond ((not (vm-new-flag m))
  1730.                (insert-before-markers
  1731.                 vm-berkeley-mail-status-header
  1732.                 (if (vm-unread-flag m) "" "R")
  1733.                 "O\n")
  1734.                (set-marker (vm-headers-of m) opoint)))))
  1735.          (vm-set-modflag-of m nil))
  1736.      (set-buffer-modified-p old-buffer-modified-p))))))
  1737.  
  1738. (defun vm-stuff-folder-attributes (&optional abort-if-input-pending)
  1739.   (let ((newlist nil) mp)
  1740.     ;; stuff the attributes of messages that need it.
  1741.     ;; build a list of messages that need their attributes stuffed
  1742.     (setq mp vm-message-list)
  1743.     (while mp
  1744.       (if (vm-modflag-of (car mp))
  1745.       (setq newlist (cons (car mp) newlist)))
  1746.       (setq mp (cdr mp)))
  1747.     ;; now sort the list by physical order so that we
  1748.     ;; reduce the amount of gap motion induced by modifying
  1749.     ;; the buffer.  what we want to avoid is updating
  1750.     ;; message 3, then 234, then 10, then 500, thus causing
  1751.     ;; large chunks of memory to be copied repeatedly as
  1752.     ;; the gap moves to accomodate the insertions.
  1753.     (let ((vm-key-functions '(vm-sort-compare-physical-order-r)))
  1754.       (setq mp (sort newlist 'vm-sort-compare-xxxxxx)))
  1755.     (while (and mp (or (not abort-if-input-pending) (not (input-pending-p))))
  1756.       (vm-stuff-attributes (car mp))
  1757.       (setq mp (cdr mp)))
  1758.     (if mp nil t)))
  1759.  
  1760. ;; we can be a bit lazy in this function since it's only called
  1761. ;; from within vm-stuff-attributes.  we don't worry about
  1762. ;; restoring the modified flag, setting buffer-read-only, or
  1763. ;; about not moving point.
  1764. (defun vm-stuff-babyl-attributes (m for-other-folder)
  1765.   (goto-char (vm-start-of m))
  1766.   (forward-char 2)
  1767.   (if (vm-babyl-frob-flag-of m)
  1768.       (insert "1")
  1769.     (insert "0"))
  1770.   (delete-char 1)
  1771.   (forward-char 1)
  1772.   (if (looking-at "\\( [^\000-\040,\177-\377]+,\\)+")
  1773.       (delete-region (match-beginning 0) (match-end 0)))
  1774.   (if (vm-new-flag m)
  1775.       (insert " recent, unseen,")
  1776.     (if (vm-unread-flag m)
  1777.     (insert " unseen,")))
  1778.   (if (and (not for-other-folder) (vm-deleted-flag m))
  1779.       (insert " deleted,"))
  1780.   (if (vm-replied-flag m)
  1781.       (insert " answered,"))
  1782.   (if (vm-forwarded-flag m)
  1783.       (insert " forwarded,"))
  1784.   (if (vm-redistributed-flag m)
  1785.       (insert " redistributed,"))
  1786.   (if (vm-filed-flag m)
  1787.       (insert " filed,"))
  1788.   (if (vm-edited-flag m)
  1789.       (insert " edited,"))
  1790.   (if (vm-written-flag m)
  1791.       (insert " written,"))
  1792.   (forward-char 1)
  1793.   (if (looking-at "\\( [^\000-\040,\177-\377]+,\\)+")
  1794.       (delete-region (match-beginning 0) (match-end 0)))
  1795.   (mapcar (function (lambda (label) (insert " " label ",")))
  1796.       (vm-labels-of m)))
  1797.  
  1798. (defun vm-babyl-attributes-string (m for-other-folder)
  1799.   (concat
  1800.    (if (vm-new-flag m)
  1801.        " recent, unseen,"
  1802.      (if (vm-unread-flag m)
  1803.      " unseen,"))
  1804.    (if (and (not for-other-folder) (vm-deleted-flag m))
  1805.        " deleted,")
  1806.    (if (vm-replied-flag m)
  1807.        " answered,")
  1808.    (if (vm-forwarded-flag m)
  1809.        " forwarded,")
  1810.    (if (vm-redistributed-flag m)
  1811.        " redistributed,")
  1812.    (if (vm-filed-flag m)
  1813.        " filed,")
  1814.    (if (vm-edited-flag m)
  1815.        " edited,")
  1816.    (if (vm-written-flag m)
  1817.        " written,")))
  1818.  
  1819. (defun vm-babyl-labels-string (m)
  1820.   (let ((list nil)
  1821.     (labels (vm-labels-of m)))
  1822.     (while labels
  1823.       (setq list (cons "," (cons (car labels) (cons " " list)))
  1824.         labels (cdr labels)))
  1825.     (apply 'concat (nreverse list))))
  1826.  
  1827. (defun vm-stuff-virtual-attributes (message)
  1828.   (let ((virtual (vm-virtual-message-p message)))
  1829.     (if (or (not virtual) (and virtual (vm-virtual-messages-of message)))
  1830.     (save-excursion
  1831.       (set-buffer
  1832.        (vm-buffer-of
  1833.         (vm-real-message-of message)))
  1834.       (vm-stuff-attributes (vm-real-message-of message))))))
  1835.  
  1836. (defun vm-stuff-labels ()
  1837.   (if vm-message-pointer
  1838.       (save-excursion
  1839.     (vm-save-restriction
  1840.      (widen)
  1841.      (let ((old-buffer-modified-p (buffer-modified-p))
  1842.            (case-fold-search t)
  1843.            ;; don't truncate the printing of large Lisp objects
  1844.            (print-length nil)
  1845.            ;; This prevents file locking from occuring.  Disabling
  1846.            ;; locking can speed things noticeably if the lock
  1847.            ;; directory is on a slow device.  We don't need locking
  1848.            ;; here because the user shouldn't care about VM stuffing
  1849.            ;; its own status headers.
  1850.            (buffer-file-name nil)
  1851.            (buffer-read-only nil)
  1852.            lim)
  1853.        (if (eq vm-folder-type 'babyl)
  1854.            (progn
  1855.          (goto-char (point-min))
  1856.          (vm-skip-past-folder-header)
  1857.          (delete-region (point) (point-min))
  1858.          (insert-before-markers (vm-folder-header vm-folder-type
  1859.                               vm-label-obarray))))
  1860.        (goto-char (point-min))
  1861.        (vm-skip-past-folder-header)
  1862.        (vm-find-leading-message-separator)
  1863.        (vm-skip-past-leading-message-separator)
  1864.        (search-forward "\n\n" nil t)
  1865.        (setq lim (point))
  1866.        (goto-char (point-min))
  1867.        (vm-skip-past-folder-header)
  1868.        (vm-find-leading-message-separator)
  1869.        (vm-skip-past-leading-message-separator)
  1870.        (while (re-search-forward vm-labels-header-regexp lim t)
  1871.          (progn (goto-char (match-beginning 0))
  1872.             (if (vm-match-header vm-labels-header)
  1873.             (delete-region (vm-matched-header-start)
  1874.                        (vm-matched-header-end)))))
  1875.        ;; To insert or to insert-before-markers, that is the question.
  1876.        ;;
  1877.        ;; If we insert-before-markers we push a header behind
  1878.        ;; vm-headers-of, which is clearly undesirable.  So we
  1879.        ;; just insert.  This will cause the summary header
  1880.        ;; to be visible if there are no non-visible headers,
  1881.        ;; oh well, no way around this.
  1882.        (insert vm-labels-header " "
  1883.            (let ((print-escape-newlines t)
  1884.              (list nil))
  1885.              (mapatoms (function
  1886.                 (lambda (sym)
  1887.                   (setq list (cons (symbol-name sym) list))))
  1888.                    vm-label-obarray)
  1889.              (prin1-to-string list))
  1890.            "\n")
  1891.        (set-buffer-modified-p old-buffer-modified-p))))))
  1892.  
  1893. ;; Insert a bookmark into the first message in the folder.
  1894. (defun vm-stuff-bookmark ()
  1895.   (if vm-message-pointer
  1896.       (save-excursion
  1897.     (vm-save-restriction
  1898.      (widen)
  1899.      (let ((old-buffer-modified-p (buffer-modified-p))
  1900.            (case-fold-search t)
  1901.            ;; This prevents file locking from occuring.  Disabling
  1902.            ;; locking can speed things noticeably if the lock
  1903.            ;; directory is on a slow device.  We don't need locking
  1904.            ;; here because the user shouldn't care about VM stuffing
  1905.            ;; its own status headers.
  1906.            (buffer-file-name nil)
  1907.            (buffer-read-only nil)
  1908.            lim)
  1909.        (goto-char (point-min))
  1910.        (vm-skip-past-folder-header)
  1911.        (vm-find-leading-message-separator)
  1912.        (vm-skip-past-leading-message-separator)
  1913.        (search-forward "\n\n" nil t)
  1914.        (setq lim (point))
  1915.        (goto-char (point-min))
  1916.        (vm-skip-past-folder-header)
  1917.        (vm-find-leading-message-separator)
  1918.        (vm-skip-past-leading-message-separator)
  1919.        (if (re-search-forward vm-bookmark-header-regexp lim t)
  1920.            (progn (goto-char (match-beginning 0))
  1921.               (if (vm-match-header vm-bookmark-header)
  1922.               (delete-region (vm-matched-header-start)
  1923.                      (vm-matched-header-end)))))
  1924.        ;; To insert or to insert-before-markers, that is the question.
  1925.        ;;
  1926.        ;; If we insert-before-markers we push a header behind
  1927.        ;; vm-headers-of, which is clearly undesirable.  So we
  1928.        ;; just insert.  This will cause the bookmark header
  1929.        ;; to be visible if there are no non-visible headers,
  1930.        ;; oh well, no way around this.
  1931.        (insert vm-bookmark-header " "
  1932.            (vm-number-of (car vm-message-pointer))
  1933.            "\n")
  1934.        (set-buffer-modified-p old-buffer-modified-p))))))
  1935.  
  1936. (defun vm-stuff-last-modified ()
  1937.   (if vm-message-pointer
  1938.       (save-excursion
  1939.     (vm-save-restriction
  1940.      (widen)
  1941.      (let ((old-buffer-modified-p (buffer-modified-p))
  1942.            (case-fold-search t)
  1943.            ;; This prevents file locking from occuring.  Disabling
  1944.            ;; locking can speed things noticeably if the lock
  1945.            ;; directory is on a slow device.  We don't need locking
  1946.            ;; here because the user shouldn't care about VM stuffing
  1947.            ;; its own status headers.
  1948.            (buffer-file-name nil)
  1949.            (buffer-read-only nil)
  1950.            lim)
  1951.        (goto-char (point-min))
  1952.        (vm-skip-past-folder-header)
  1953.        (vm-find-leading-message-separator)
  1954.        (vm-skip-past-leading-message-separator)
  1955.        (search-forward "\n\n" nil t)
  1956.        (setq lim (point))
  1957.        (goto-char (point-min))
  1958.        (vm-skip-past-folder-header)
  1959.        (vm-find-leading-message-separator)
  1960.        (vm-skip-past-leading-message-separator)
  1961.        (if (re-search-forward vm-last-modified-header-regexp lim t)
  1962.            (progn (goto-char (match-beginning 0))
  1963.               (if (vm-match-header vm-last-modified-header)
  1964.               (delete-region (vm-matched-header-start)
  1965.                      (vm-matched-header-end)))))
  1966.        ;; To insert or to insert-before-markers, that is the question.
  1967.        ;;
  1968.        ;; If we insert-before-markers we push a header behind
  1969.        ;; vm-headers-of, which is clearly undesirable.  So we
  1970.        ;; just insert.  This will cause the last-modified header
  1971.        ;; to be visible if there are no non-visible headers,
  1972.        ;; oh well, no way around this.
  1973.        (insert vm-last-modified-header " "
  1974.            (prin1-to-string (current-time))
  1975.            "\n")
  1976.        (set-buffer-modified-p old-buffer-modified-p))))))
  1977.  
  1978. (defun vm-stuff-pop-retrieved ()
  1979.   (if vm-message-pointer
  1980.       (save-excursion
  1981.     (vm-save-restriction
  1982.      (widen)
  1983.      (let ((old-buffer-modified-p (buffer-modified-p))
  1984.            (case-fold-search t)
  1985.            ;; This prevents file locking from occuring.  Disabling
  1986.            ;; locking can speed things noticeably if the lock
  1987.            ;; directory is on a slow device.  We don't need locking
  1988.            ;; here because the user shouldn't care about VM stuffing
  1989.            ;; its own status headers.
  1990.            (buffer-file-name nil)
  1991.            (buffer-read-only nil)
  1992.            (print-length nil)
  1993.            (p vm-pop-retrieved-messages)
  1994.            (curbuf (current-buffer))
  1995.            lim)
  1996.        (goto-char (point-min))
  1997.        (vm-skip-past-folder-header)
  1998.        (vm-find-leading-message-separator)
  1999.        (vm-skip-past-leading-message-separator)
  2000.        (search-forward "\n\n" nil t)
  2001.        (setq lim (point))
  2002.        (goto-char (point-min))
  2003.        (vm-skip-past-folder-header)
  2004.        (vm-find-leading-message-separator)
  2005.        (vm-skip-past-leading-message-separator)
  2006.        (if (re-search-forward vm-pop-retrieved-header-regexp lim t)
  2007.            (progn (goto-char (match-beginning 0))
  2008.               (if (vm-match-header vm-pop-retrieved-header)
  2009.               (delete-region (vm-matched-header-start)
  2010.                      (vm-matched-header-end)))))
  2011.        ;; To insert or to insert-before-markers, that is the question.
  2012.        ;;
  2013.        ;; If we insert-before-markers we push a header behind
  2014.        ;; vm-headers-of, which is clearly undesirable.  So we
  2015.        ;; just insert.  This will cause the pop-retrieved header
  2016.        ;; to be visible if there are no non-visible headers,
  2017.        ;; oh well, no way around this.
  2018.        (insert vm-pop-retrieved-header)
  2019.        (if (null p)
  2020.            (insert " nil\n")
  2021.          (insert "\n   (\n")
  2022.          (while p
  2023.            (insert "\t")
  2024.            (prin1 (car p) curbuf)
  2025.            (insert "\n")
  2026.            (setq p (cdr p)))
  2027.          (insert "   )\n"))
  2028.        (set-buffer-modified-p old-buffer-modified-p))))))
  2029.  
  2030. ;; Insert the summary format variable header into the first message.
  2031. (defun vm-stuff-summary ()
  2032.   (if vm-message-pointer
  2033.       (save-excursion
  2034.     (vm-save-restriction
  2035.      (widen)
  2036.      (let ((old-buffer-modified-p (buffer-modified-p))
  2037.            (case-fold-search t)
  2038.            ;; don't truncate the printing of large Lisp objects
  2039.            (print-length nil)
  2040.            ;; This prevents file locking from occuring.  Disabling
  2041.            ;; locking can speed things noticeably if the lock
  2042.            ;; directory is on a slow device.  We don't need locking
  2043.            ;; here because the user shouldn't care about VM stuffing
  2044.            ;; its own status headers.
  2045.            (buffer-file-name nil)
  2046.            (buffer-read-only nil)
  2047.            lim)
  2048.        (goto-char (point-min))
  2049.        (vm-skip-past-folder-header)
  2050.        (vm-find-leading-message-separator)
  2051.        (vm-skip-past-leading-message-separator)
  2052.        (search-forward "\n\n" nil t)
  2053.        (setq lim (point))
  2054.        (goto-char (point-min))
  2055.        (vm-skip-past-folder-header)
  2056.        (vm-find-leading-message-separator)
  2057.        (vm-skip-past-leading-message-separator)
  2058.        (while (re-search-forward vm-summary-header-regexp lim t)
  2059.          (progn (goto-char (match-beginning 0))
  2060.             (if (vm-match-header vm-summary-header)
  2061.             (delete-region (vm-matched-header-start)
  2062.                        (vm-matched-header-end)))))
  2063.        ;; To insert or to insert-before-markers, that is the question.
  2064.        ;;
  2065.        ;; If we insert-before-markers we push a header behind
  2066.        ;; vm-headers-of, which is clearly undesirable.  So we
  2067.        ;; just insert.  This will cause the summary header
  2068.        ;; to be visible if there are no non-visible headers,
  2069.        ;; oh well, no way around this.
  2070.        (insert vm-summary-header " "
  2071.            (let ((print-escape-newlines t))
  2072.              (prin1-to-string vm-summary-format))
  2073.            "\n")
  2074.        (set-buffer-modified-p old-buffer-modified-p))))))
  2075.  
  2076. ;; stuff the current values of the header variables for future messages.
  2077. (defun vm-stuff-header-variables ()
  2078.   (if vm-message-pointer
  2079.       (save-excursion
  2080.     (vm-save-restriction
  2081.      (widen)
  2082.      (let ((old-buffer-modified-p (buffer-modified-p))
  2083.            (case-fold-search t)
  2084.            (print-escape-newlines t)
  2085.            lim
  2086.            ;; don't truncate the printing of large Lisp objects
  2087.            (print-length nil)
  2088.            (buffer-read-only nil)
  2089.            ;; This prevents file locking from occuring.  Disabling
  2090.            ;; locking can speed things noticeably if the lock
  2091.            ;; directory is on a slow device.  We don't need locking
  2092.            ;; here because the user shouldn't care about VM stuffing
  2093.            ;; its own status headers.
  2094.            (buffer-file-name nil))
  2095.        (goto-char (point-min))
  2096.        (vm-skip-past-folder-header)
  2097.        (vm-find-leading-message-separator)
  2098.        (vm-skip-past-leading-message-separator)
  2099.        (search-forward "\n\n" nil t)
  2100.        (setq lim (point))
  2101.        (goto-char (point-min))
  2102.        (vm-skip-past-folder-header)
  2103.        (vm-find-leading-message-separator)
  2104.        (vm-skip-past-leading-message-separator)
  2105.        (while (re-search-forward vm-vheader-header-regexp lim t)
  2106.          (progn (goto-char (match-beginning 0))
  2107.             (if (vm-match-header vm-vheader-header)
  2108.             (delete-region (vm-matched-header-start)
  2109.                        (vm-matched-header-end)))))
  2110.        ;; To insert or to insert-before-markers, that is the question.
  2111.        ;;
  2112.        ;; If we insert-before-markers we push a header behind
  2113.        ;; vm-headers-of, which is clearly undesirable.  So we
  2114.        ;; just insert.  This header will be visible if there
  2115.        ;; are no non-visible headers, oh well, no way around this.
  2116.        (insert vm-vheader-header " "
  2117.            (prin1-to-string vm-visible-headers) " "
  2118.            (prin1-to-string vm-invisible-header-regexp)
  2119.            "\n")
  2120.        (set-buffer-modified-p old-buffer-modified-p))))))
  2121.  
  2122. ;; Insert a header into the first message of the folder that lists
  2123. ;; the folder's message order.
  2124. (defun vm-stuff-message-order ()
  2125.   (if (cdr vm-message-list)
  2126.       (save-excursion
  2127.     (vm-save-restriction
  2128.      (widen)
  2129.      (let ((old-buffer-modified-p (buffer-modified-p))
  2130.            (case-fold-search t)
  2131.            ;; This prevents file locking from occuring.  Disabling
  2132.            ;; locking can speed things noticeably if the lock
  2133.            ;; directory is on a slow device.  We don't need locking
  2134.            ;; here because the user shouldn't care about VM stuffing
  2135.            ;; its own status headers.
  2136.            (buffer-file-name nil)
  2137.            lim n
  2138.            (buffer-read-only nil)
  2139.            (mp (copy-sequence vm-message-list)))
  2140.        (setq mp
  2141.          (sort mp
  2142.                (function
  2143.             (lambda (p q)
  2144.               (< (vm-start-of p) (vm-start-of q))))))
  2145.        (goto-char (point-min))
  2146.        (vm-skip-past-folder-header)
  2147.        (vm-find-leading-message-separator)
  2148.        (vm-skip-past-leading-message-separator)
  2149.        (search-forward "\n\n" nil t)
  2150.        (setq lim (point))
  2151.        (goto-char (point-min))
  2152.        (vm-skip-past-folder-header)
  2153.        (vm-find-leading-message-separator)
  2154.        (vm-skip-past-leading-message-separator)
  2155.        (while (re-search-forward vm-message-order-header-regexp lim t)
  2156.          (progn (goto-char (match-beginning 0))
  2157.             (if (vm-match-header vm-message-order-header)
  2158.             (delete-region (vm-matched-header-start)
  2159.                        (vm-matched-header-end)))))
  2160.        ;; To insert or to insert-before-markers, that is the question.
  2161.        ;;
  2162.        ;; If we insert-before-markers we push a header behind
  2163.        ;; vm-headers-of, which is clearly undesirable.  So we
  2164.        ;; just insert.  This header will be visible if there
  2165.        ;; are no non-visible headers, oh well, no way around this.
  2166.        (insert vm-message-order-header "\n\t(")
  2167.        (setq n 0)
  2168.        (while mp
  2169.          (insert (vm-number-of (car mp)))
  2170.          (setq n (1+ n) mp (cdr mp))
  2171.          (and mp (insert
  2172.               (if (zerop (% n 15))
  2173.               "\n\t "
  2174.             " "))))
  2175.        (insert ")\n")
  2176.        (setq vm-message-order-changed nil
  2177.          vm-message-order-header-present t)
  2178.        (set-buffer-modified-p old-buffer-modified-p))))))
  2179.  
  2180. ;; Remove the message order header.
  2181. (defun vm-remove-message-order ()
  2182.   (if (cdr vm-message-list)
  2183.       (save-excursion
  2184.     (vm-save-restriction
  2185.      (widen)
  2186.      (let ((old-buffer-modified-p (buffer-modified-p))
  2187.            (case-fold-search t)
  2188.            lim
  2189.            ;; This prevents file locking from occuring.  Disabling
  2190.            ;; locking can speed things noticeably if the lock
  2191.            ;; directory is on a slow device.  We don't need locking
  2192.            ;; here because the user shouldn't care about VM stuffing
  2193.            ;; its own status headers.
  2194.            (buffer-file-name nil)
  2195.            (buffer-read-only nil))
  2196.        (goto-char (point-min))
  2197.        (vm-skip-past-folder-header)
  2198.        (vm-skip-past-leading-message-separator)
  2199.        (search-forward "\n\n" nil t)
  2200.        (setq lim (point))
  2201.        (goto-char (point-min))
  2202.        (vm-skip-past-folder-header)
  2203.        (vm-skip-past-leading-message-separator)
  2204.        (while (re-search-forward vm-message-order-header-regexp lim t)
  2205.          (progn (goto-char (match-beginning 0))
  2206.             (if (vm-match-header vm-message-order-header)
  2207.             (delete-region (vm-matched-header-start)
  2208.                        (vm-matched-header-end)))))
  2209.        (setq vm-message-order-header-present nil)
  2210.        (set-buffer-modified-p old-buffer-modified-p))))))
  2211.  
  2212. (defun vm-read-index-file-maybe ()
  2213.   (catch 'done
  2214.     (if (or (not (stringp buffer-file-name))
  2215.         (not (stringp vm-index-file-suffix)))
  2216.     (throw 'done nil))
  2217.     (let ((index-file (concat buffer-file-name vm-index-file-suffix)))
  2218.       (if (file-readable-p index-file)
  2219.       (vm-read-index-file index-file)
  2220.     nil ))))
  2221.  
  2222. (defun vm-read-index-file (index-file)
  2223.   (catch 'done
  2224.     (condition-case error-data
  2225.     (let ((work-buffer nil))
  2226.       (unwind-protect
  2227.           (let (obj attr-list cache-list location-list label-list
  2228.             validity-check vis invis folder-type
  2229.             bookmark summary labels retrieved order
  2230.             v m (m-list nil) tail)
  2231.         (message "Reading index file...")
  2232.         (setq work-buffer (vm-make-work-buffer))
  2233.         (save-excursion
  2234.           (set-buffer work-buffer)
  2235.           (insert-file-contents-literally index-file))
  2236.         (goto-char (point-min))
  2237.  
  2238.         ;; check version
  2239.         (setq obj (read work-buffer))
  2240.         (if (not (eq obj 1))
  2241.             (error "Unsupported index file version: %s") obj)
  2242.  
  2243.         ;; folder type
  2244.         (setq folder-type (read work-buffer))
  2245.  
  2246.         ;; validity check
  2247.         (setq validity-check (read work-buffer))
  2248.         (if (null (vm-check-index-file-validity validity-check))
  2249.             (throw 'done nil))
  2250.  
  2251.         ;; bookmark
  2252.         (setq bookmark (read work-buffer))
  2253.  
  2254.         ;; message order
  2255.         (setq order (read work-buffer))
  2256.  
  2257.         ;; what summary format was used to produce the
  2258.         ;; folder's summary cache line.
  2259.         (setq summary (read work-buffer))
  2260.  
  2261.         ;; folder-wide list of labels
  2262.         (setq labels (read work-buffer))
  2263.  
  2264.         ;; what vm-visible-headers / vm-invisible-header-regexp
  2265.         ;; settings were used to order the headers and to
  2266.         ;; produce the vm-headers-regexp-of slot value.
  2267.         (setq vis (read work-buffer))
  2268.         (setq invis (read work-buffer))
  2269.  
  2270.         ;; location offsets
  2271.         ;; attributes list
  2272.         ;; cache list
  2273.         ;; label list
  2274.         (setq location-list (read work-buffer))
  2275.         (setq attr-list (read work-buffer))
  2276.         (setq cache-list (read work-buffer))
  2277.         (setq label-list (read work-buffer))
  2278.         (while location-list
  2279.           (setq v (car location-list)
  2280.             m (vm-make-message))
  2281.           (if (null m-list)
  2282.               (setq m-list (list m)
  2283.                 tail m-list)
  2284.             (setcdr tail (list m))
  2285.             (setq tail (cdr tail)))
  2286.           (vm-set-start-of m (vm-marker (aref v 0)))
  2287.           (vm-set-headers-of m (vm-marker (aref v 1)))
  2288.           (vm-set-text-end-of m (vm-marker (aref v 2)))
  2289.           (vm-set-end-of m (vm-marker (aref v 3)))
  2290.           (if (null attr-list)
  2291.               (error "Attribute list is shorter than location list")
  2292.             (setq v (car attr-list))
  2293.             (if (< (length v) vm-attributes-vector-length)
  2294.             (setq v (vm-extend-vector
  2295.                  v vm-attributes-vector-length)))
  2296.             (vm-set-attributes-of m v))
  2297.           (if (null cache-list)
  2298.               (error "Cache list is shorter than location list")
  2299.             (setq v (car cache-list))
  2300.             (if (< (length v) vm-cache-vector-length)
  2301.             (setq v (vm-extend-vector v vm-cache-vector-length)))
  2302.             (vm-set-cache-of m v))
  2303.           (if (null label-list)
  2304.               (error "Label list is shorter than location list")
  2305.             (vm-set-labels-of m (car label-list)))
  2306.           (setq location-list (cdr location-list)
  2307.             attr-list (cdr attr-list)
  2308.             cache-list (cdr cache-list)
  2309.             label-list (cdr label-list)))
  2310.  
  2311.         ;; pop retrieved messages
  2312.         (setq retrieved (read work-buffer))
  2313.  
  2314.         (setq vm-message-list m-list
  2315.               vm-folder-type folder-type
  2316.               vm-pop-retrieved-messages retrieved)
  2317.  
  2318.         (vm-startup-apply-bookmark bookmark)
  2319.         (and order (vm-startup-apply-message-order order))
  2320.         (vm-startup-apply-summary summary)
  2321.         (vm-startup-apply-labels labels)
  2322.         (vm-startup-apply-header-variables vis invis)
  2323.  
  2324.         (message "Reading index file... done")
  2325.         t )
  2326.         (and work-buffer (kill-buffer work-buffer))))
  2327.       (error (message "Index file read of %s signaled: %s"
  2328.               index-file error-data)
  2329.          (sleep-for 2)
  2330.          (message "Ignoring index file...")
  2331.          (sleep-for 2)))))
  2332.  
  2333. (defun vm-check-index-file-validity (blob)
  2334.   (save-excursion
  2335.     (widen)
  2336.     (catch 'done
  2337.       (cond ((not (consp blob))
  2338.          (error "Validity check object not a cons: %s"))
  2339.         ((eq (car blob) 'file)
  2340.          (let (ch time time2)
  2341.            (setq blob (cdr blob))
  2342.            (setq time (car blob)
  2343.              time2 (vm-gobble-last-modified))
  2344.            (if (> 0 (vm-time-difference time time2))
  2345.            (throw 'done nil))
  2346.            (setq blob (cdr blob))
  2347.            (while blob
  2348.          (setq ch (char-after (car blob)))
  2349.          (if (or (null ch) (not (eq (vm-char-to-int ch) (nth 1 blob))))
  2350.              (throw 'done nil))
  2351.          (setq blob (cdr (cdr blob)))))
  2352.          t )
  2353.         (t (error "Unknown validity check type: %s" (car blob)))))))
  2354.  
  2355. (defun vm-generate-index-file-validity-check ()
  2356.   (save-restriction
  2357.     (widen)
  2358.     (let ((step (/ (point-max) 11))
  2359.       (pos (1- (point-max)))
  2360.       (lim (point-min))
  2361.       (blob nil))
  2362.       (while (>= pos lim)
  2363.     (setq blob (cons pos (cons (vm-char-to-int (char-after pos)) blob))
  2364.           pos (- pos step)))
  2365.       (cons 'file (cons (current-time) blob)))))
  2366.  
  2367. (defun vm-write-index-file-maybe ()
  2368.   (catch 'done
  2369.     (if (not (stringp buffer-file-name))
  2370.     (throw 'done nil))
  2371.     (if (not (stringp vm-index-file-suffix))
  2372.     (throw 'done nil))
  2373.     (let ((index-file (concat buffer-file-name vm-index-file-suffix)))
  2374.       (vm-write-index-file index-file))))
  2375.  
  2376. (defun vm-write-index-file (index-file)
  2377.   (let ((work-buffer nil))
  2378.     (unwind-protect
  2379.     (let ((print-escape-newlines t)
  2380.           (print-length nil)
  2381.           m-list mp m)
  2382.       (message "Sorting for index file...")
  2383.       (setq m-list (sort (copy-sequence vm-message-list)
  2384.                  (function vm-sort-compare-physical-order)))
  2385.       (message "Stuffing index file...")
  2386.       (setq work-buffer (vm-make-work-buffer))
  2387.  
  2388.       (princ ";; index file version\n" work-buffer)
  2389.       (prin1 1 work-buffer)
  2390.       (terpri work-buffer)
  2391.  
  2392.       (princ ";; folder type\n" work-buffer)
  2393.       (prin1 vm-folder-type work-buffer)
  2394.       (terpri work-buffer)
  2395.  
  2396.       (princ 
  2397.        ";; timestamp + sample of folder bytes for consistency check\n"
  2398.        work-buffer)
  2399.       (prin1 (vm-generate-index-file-validity-check) work-buffer)
  2400.       (terpri work-buffer)
  2401.  
  2402.       (princ ";; bookmark\n" work-buffer)
  2403.       (princ (if vm-message-pointer
  2404.              (vm-number-of (car vm-message-pointer))
  2405.            "1")
  2406.          work-buffer)
  2407.       (terpri work-buffer)
  2408.  
  2409.       (princ ";; message order\n" work-buffer)
  2410.       (let ((n 0) (mp vm-message-list))
  2411.        (princ "(" work-buffer)
  2412.        (setq n 0)
  2413.        (while mp
  2414.          (if (zerop (% n 15))
  2415.          (princ "\n\t" work-buffer)
  2416.            (princ " " work-buffer))
  2417.          (princ (vm-number-of (car mp)) work-buffer)
  2418.          (setq n (1+ n) mp (cdr mp)))
  2419.        (princ "\n)\n" work-buffer))
  2420.  
  2421.       (princ ";; summary\n" work-buffer)
  2422.       (prin1 vm-summary-format work-buffer)
  2423.       (terpri work-buffer)
  2424.  
  2425.       (princ ";; labels used in this folder\n" work-buffer)
  2426.       (let ((list nil))
  2427.         (mapatoms (function
  2428.                (lambda (sym)
  2429.              (setq list (cons (symbol-name sym) list))))
  2430.               vm-label-obarray)
  2431.         (prin1 list work-buffer))
  2432.       (terpri work-buffer)
  2433.  
  2434.       (princ ";; visible headers\n" work-buffer)
  2435.       (prin1 vm-visible-headers work-buffer)
  2436.       (terpri work-buffer)
  2437.  
  2438.       (princ ";; hidden headers\n" work-buffer)
  2439.       (prin1 vm-invisible-header-regexp work-buffer)
  2440.       (terpri work-buffer)
  2441.  
  2442.       (princ ";; location list\n" work-buffer)
  2443.       (princ "(\n" work-buffer)
  2444.       (setq mp m-list)
  2445.       (while mp
  2446.         (setq m (car mp))
  2447.         (princ "  [" work-buffer)
  2448.         (prin1 (marker-position (vm-start-of m)) work-buffer)
  2449.         (princ " " work-buffer)
  2450.         (prin1 (marker-position (vm-headers-of m)) work-buffer)
  2451.         (princ " " work-buffer)
  2452.         (prin1 (marker-position (vm-text-end-of m)) work-buffer)
  2453.         (princ " " work-buffer)
  2454.         (prin1 (marker-position (vm-end-of m)) work-buffer)
  2455.         (princ "]\n" work-buffer)
  2456.         (setq mp (cdr mp)))
  2457.       (princ ")\n" work-buffer)
  2458.       (princ ";; attribute list\n" work-buffer)
  2459.       (princ "(\n" work-buffer)
  2460.       (setq mp m-list)
  2461.       (while mp
  2462.         (setq m (car mp))
  2463.         (princ "  " work-buffer)
  2464.         (prin1 (vm-attributes-of m) work-buffer)
  2465.         (princ "\n" work-buffer)
  2466.         (setq mp (cdr mp)))
  2467.       (princ ")\n" work-buffer)
  2468.       (princ ";; cache list\n" work-buffer)
  2469.       (princ "(\n" work-buffer)
  2470.       (setq mp m-list)
  2471.       (while mp
  2472.         (setq m (car mp))
  2473.         (princ "  " work-buffer)
  2474.         (prin1 (vm-cache-of m) work-buffer)
  2475.         (princ "\n" work-buffer)
  2476.         (setq mp (cdr mp)))
  2477.       (princ ")\n" work-buffer)
  2478.       (princ ";; labels list\n" work-buffer)
  2479.       (princ "(\n" work-buffer)
  2480.       (setq mp m-list)
  2481.       (while mp
  2482.         (setq m (car mp))
  2483.         (princ "  " work-buffer)
  2484.         (prin1 (vm-labels-of m) work-buffer)
  2485.         (princ "\n" work-buffer)
  2486.         (setq mp (cdr mp)))
  2487.       (princ ")\n" work-buffer)
  2488.       (princ ";; retrieved POP messages\n" work-buffer)
  2489.       (let ((p vm-pop-retrieved-messages))
  2490.         (if (null p)
  2491.         (princ "nil\n" work-buffer)
  2492.           (princ "(\n" work-buffer)
  2493.           (while p
  2494.         (princ "\t" work-buffer)
  2495.         (prin1 (car p) work-buffer)
  2496.         (princ "\n" work-buffer)
  2497.         (setq p (cdr p)))
  2498.           (princ ")\n" work-buffer)))
  2499.  
  2500.       (princ ";; end of index file\n" work-buffer)
  2501.  
  2502.       (message "Writing index file...")
  2503.       (catch 'done
  2504.         (save-excursion
  2505.           (set-buffer work-buffer)
  2506.           (condition-case data
  2507.           (let ((coding-system-for-write 'binary))
  2508.             (write-region (point-min) (point-max) index-file))
  2509.         (error
  2510.          (message "Write of %s signaled: %s" index-file data)
  2511.          (sleep-for 2)
  2512.          (throw 'done nil))))
  2513.         (vm-error-free-call 'set-file-modes index-file 384) ;; 384 == 0600
  2514.         (message "Writing index file... done")
  2515.         t ))
  2516.       (and work-buffer (kill-buffer work-buffer)))))
  2517.  
  2518. (defun vm-delete-index-file ()
  2519.   (let ((index-file (concat buffer-file-name vm-index-file-suffix)))
  2520.     (vm-error-free-call 'delete-file index-file)))
  2521.  
  2522. (defun vm-change-all-new-to-unread ()
  2523.   (let ((mp vm-message-list))
  2524.     (while mp
  2525.       (if (vm-new-flag (car mp))
  2526.       (progn
  2527.         (vm-set-new-flag (car mp) nil)
  2528.         (vm-set-unread-flag (car mp) t)))
  2529.       (setq mp (cdr mp)))))
  2530.  
  2531. (defun vm-unread-message (&optional count)
  2532.   "Set the `unread' attribute for the current message.  If the message is
  2533. already new or unread, then it is left unchanged.
  2534.  
  2535. Numeric prefix argument N means to unread the current message plus the
  2536. next N-1 messages.  A negative N means unread the current message and
  2537. the previous N-1 messages.
  2538.  
  2539. When invoked on marked messages (via vm-next-command-uses-marks),
  2540. all marked messages are affected, other messages are ignored."
  2541.   (interactive "p")
  2542.   (or count (setq count 1))
  2543.   (vm-follow-summary-cursor)
  2544.   (vm-select-folder-buffer)
  2545.   (vm-check-for-killed-summary)
  2546.   (vm-error-if-folder-empty)
  2547.   (let ((mlist (vm-select-marked-or-prefixed-messages count)))
  2548.     (while mlist
  2549.       (if (and (not (vm-unread-flag (car mlist)))
  2550.            (not (vm-new-flag (car mlist))))
  2551.       (vm-set-unread-flag (car mlist) t))
  2552.       (setq mlist (cdr mlist))))
  2553.   (vm-display nil nil '(vm-unread-message) '(vm-unread-message))
  2554.   (vm-update-summary-and-mode-line))
  2555.  
  2556. (defun vm-quit-just-bury ()
  2557.   "Bury the current VM folder and summary buffers.
  2558. The folder is not altered and Emacs is still visiting it.  You
  2559. can switch back to it with switch-to-buffer or by using the
  2560. Buffer Menu."
  2561.   (interactive)
  2562.   (vm-select-folder-buffer)
  2563.   (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
  2564.       (error "%s must be invoked from a VM buffer." this-command))
  2565.   (vm-check-for-killed-summary)
  2566.   (vm-check-for-killed-presentation)
  2567.  
  2568.   (save-excursion (run-hooks 'vm-quit-hook))
  2569.  
  2570.   (vm-garbage-collect-message)
  2571.  
  2572.   (vm-display nil nil '(vm-quit-just-bury)
  2573.           '(vm-quit-just-bury quitting))
  2574.   (if vm-summary-buffer
  2575.       (vm-display vm-summary-buffer nil nil nil))
  2576.   (if vm-summary-buffer
  2577.       (vm-bury-buffer vm-summary-buffer))
  2578.   (if vm-presentation-buffer-handle
  2579.       (vm-display vm-presentation-buffer-handle nil nil nil))
  2580.   (if vm-presentation-buffer-handle
  2581.       (vm-bury-buffer vm-presentation-buffer-handle))
  2582.   (vm-display (current-buffer) nil nil nil)
  2583.   (vm-bury-buffer (current-buffer)))
  2584.  
  2585. (defun vm-quit-just-iconify ()
  2586.   "Iconify the frame and bury the current VM folder and summary buffers.
  2587. The folder is not altered and Emacs is still visiting it."
  2588.   (interactive)
  2589.   (vm-select-folder-buffer)
  2590.   (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
  2591.       (error "%s must be invoked from a VM buffer." this-command))
  2592.   (vm-check-for-killed-summary)
  2593.   (vm-check-for-killed-presentation)
  2594.  
  2595.   (save-excursion (run-hooks 'vm-quit-hook))
  2596.  
  2597.   (vm-garbage-collect-message)
  2598.  
  2599.   (vm-display nil nil '(vm-quit-just-iconify)
  2600.           '(vm-quit-just-iconify quitting))
  2601.   (let ((summary-buffer vm-summary-buffer)
  2602.     (pres-buffer vm-presentation-buffer-handle))
  2603.     (vm-bury-buffer (current-buffer))
  2604.     (if summary-buffer
  2605.     (vm-bury-buffer summary-buffer))
  2606.     (if pres-buffer
  2607.     (vm-bury-buffer pres-buffer))
  2608.     (vm-iconify-frame)))
  2609.  
  2610. (defun vm-quit-no-change ()
  2611.   "Quit visiting the current folder without saving changes made to the folder."
  2612.   (interactive)
  2613.   (vm-quit t))
  2614.  
  2615. (defun vm-quit (&optional no-change)
  2616.   "Quit visiting the current folder, saving changes.  Deleted messages are not expunged."
  2617.   (interactive)
  2618.   (vm-select-folder-buffer)
  2619.   (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
  2620.       (error "%s must be invoked from a VM buffer." this-command))
  2621.   (vm-check-for-killed-summary)
  2622.   (vm-check-for-killed-presentation)
  2623.   (vm-display nil nil '(vm-quit vm-quit-no-change)
  2624.           (list this-command 'quitting))
  2625.   (let ((virtual (eq major-mode 'vm-virtual-mode)))
  2626.     (cond
  2627.      ((and (not virtual) no-change (buffer-modified-p)
  2628.        (or buffer-file-name buffer-offer-save)
  2629.        (not (zerop vm-messages-not-on-disk))
  2630.        ;; Folder may have been saved with C-x C-s and attributes may have
  2631.        ;; been changed after that; in that case vm-messages-not-on-disk
  2632.        ;; would not have been zeroed.  However, all modification flag
  2633.        ;; undos are cleared if VM actually modifies the folder buffer
  2634.        ;; (as opposed to the folder's attributes), so this can be used
  2635.        ;; to verify that there are indeed unsaved messages.
  2636.        (null (assq 'vm-set-buffer-modified-p vm-undo-record-list))
  2637.        (not
  2638.         (y-or-n-p
  2639.          (format
  2640.           "%d message%s have not been saved to disk, quit anyway? "
  2641.           vm-messages-not-on-disk
  2642.           (if (= 1 vm-messages-not-on-disk) "" "s")))))
  2643.       (error "Aborted"))
  2644.      ((and (not virtual)
  2645.        no-change
  2646.        (or buffer-file-name buffer-offer-save)
  2647.        (buffer-modified-p)
  2648.        vm-confirm-quit
  2649.        (not (y-or-n-p "There are unsaved changes, quit anyway? ")))
  2650.       (error "Aborted"))
  2651.      ((and (eq vm-confirm-quit t)
  2652.        (not (y-or-n-p "Do you really want to quit? ")))
  2653.       (error "Aborted")))
  2654.  
  2655.     (save-excursion (run-hooks 'vm-quit-hook))
  2656.  
  2657.     (vm-garbage-collect-message)
  2658.     (vm-garbage-collect-folder)
  2659.  
  2660.     (vm-virtual-quit)
  2661.     (if (and (not no-change) (not virtual))
  2662.     (progn
  2663.       ;; this could take a while, so give the user some feedback
  2664.       (message "Quitting...")
  2665.       (or vm-folder-read-only (eq major-mode 'vm-virtual-mode)
  2666.           (vm-change-all-new-to-unread))))
  2667.     (if (and (buffer-modified-p)
  2668.          (or buffer-file-name buffer-offer-save)
  2669.          (not no-change)
  2670.          (not virtual))
  2671.     (vm-save-folder))
  2672.     (message "")
  2673.     (let ((summary-buffer vm-summary-buffer)
  2674.       (pres-buffer vm-presentation-buffer-handle)
  2675.       (mail-buffer (current-buffer)))
  2676.       (if summary-buffer
  2677.       (progn
  2678.         (vm-display summary-buffer nil nil nil)
  2679.         (kill-buffer summary-buffer)))
  2680.       (if pres-buffer
  2681.       (progn
  2682.         (vm-display pres-buffer nil nil nil)
  2683.         (kill-buffer pres-buffer)))
  2684.       (set-buffer mail-buffer)
  2685.       (vm-display mail-buffer nil nil nil)
  2686.       ;; vm-display is not supposed to change the current buffer.
  2687.       ;; still it's better to be safe here.
  2688.       (set-buffer mail-buffer)
  2689.       (set-buffer-modified-p nil)
  2690.       (kill-buffer (current-buffer)))
  2691.     (vm-update-summary-and-mode-line)))
  2692.  
  2693. (defun vm-start-itimers-if-needed ()
  2694.   (cond ((and (not (natnump vm-flush-interval))
  2695.           (not (natnump vm-auto-get-new-mail))
  2696.           (not (natnump vm-mail-check-interval))))
  2697.     ((condition-case data
  2698.          (progn (require 'itimer) t)
  2699.        (error nil))
  2700.      (and (natnump vm-flush-interval) (not (get-itimer "vm-flush"))
  2701.           (start-itimer "vm-flush" 'vm-flush-itimer-function
  2702.                 vm-flush-interval nil))
  2703.      (and (natnump vm-auto-get-new-mail) (not (get-itimer "vm-get-mail"))
  2704.           (start-itimer "vm-get-mail" 'vm-get-mail-itimer-function
  2705.                 vm-auto-get-new-mail nil))
  2706.      (and (natnump vm-mail-check-interval)
  2707.           (not (get-itimer "vm-check-mail"))
  2708.           (start-itimer "vm-check-mail" 'vm-check-mail-itimer-function
  2709.                 vm-mail-check-interval nil)))
  2710.     ((condition-case data
  2711.          (progn (require 'timer) t)
  2712.        (error nil))
  2713.      (let (timer)
  2714.        (and (natnump vm-flush-interval) 
  2715.         (not (vm-timer-using 'vm-flush-itimer-function))
  2716.         (setq timer (run-at-time vm-flush-interval vm-flush-interval
  2717.                      'vm-flush-itimer-function nil))
  2718.         (timer-set-function timer 'vm-flush-itimer-function
  2719.                     (list timer)))
  2720.        (and (natnump vm-mail-check-interval) 
  2721.         (not (vm-timer-using 'vm-check-mail-itimer-function))
  2722.         (setq timer (run-at-time vm-mail-check-interval
  2723.                      vm-mail-check-interval
  2724.                      'vm-check-mail-itimer-function nil))
  2725.         (timer-set-function timer 'vm-check-mail-itimer-function
  2726.                     (list timer)))
  2727.        (and (natnump vm-auto-get-new-mail)
  2728.         (not (vm-timer-using 'vm-get-mail-itimer-function))
  2729.         (setq timer (run-at-time vm-auto-get-new-mail
  2730.                      vm-auto-get-new-mail
  2731.                      'vm-get-mail-itimer-function nil))
  2732.         (timer-set-function timer 'vm-get-mail-itimer-function
  2733.                     (list timer)))))
  2734.     (t
  2735.      (setq vm-flush-interval t
  2736.            vm-auto-get-new-mail t))))
  2737.  
  2738. (defun vm-timer-using (fun)
  2739.   (let ((p timer-list)
  2740.     (done nil))
  2741.     (while (and p (not done))
  2742.       (if (eq (aref (car p) 5) fun)
  2743.       (setq done t)
  2744.     (setq p (cdr p))))
  2745.     p ))
  2746.  
  2747. ;; support for vm-mail-check-interval
  2748. ;; if timer argument is present, this means we're using the Emacs
  2749. ;; 'timer package rather than the 'itimer package.
  2750. (defun vm-check-mail-itimer-function (&optional timer)
  2751.   ;; FSF Emacs sets this non-nil, which means the user can't
  2752.   ;; interrupt the check.  Bogus.
  2753.   (setq inhibit-quit nil)
  2754.   (if (integerp vm-mail-check-interval)
  2755.       (if timer
  2756.       (timer-set-time timer (current-time) vm-mail-check-interval)
  2757.     (set-itimer-restart current-itimer vm-mail-check-interval))
  2758.     ;; user has changed the variable value to something that
  2759.     ;; isn't a number, make the timer go away.
  2760.     (if timer
  2761.     (cancel-timer timer)
  2762.       (set-itimer-restart current-itimer nil)))
  2763.   (let ((b-list (buffer-list))
  2764.     (found-one nil)
  2765.     oldval)
  2766.     (while (and (not (input-pending-p)) b-list)
  2767.       (save-excursion
  2768.     (set-buffer (car b-list))
  2769.     (if (and (eq major-mode 'vm-mode)
  2770.          (setq found-one t)
  2771.          ;; to avoid reentrance into the pop code
  2772.          (not vm-block-new-mail)
  2773.          ;; Don't bother checking if we already know from
  2774.          ;; a previous check that there's mail waiting
  2775.          ;; and the user hasn't retrieved it yet.  Not
  2776.          ;; completely accurate, but saves network
  2777.          ;; connection build and tear down which is slow
  2778.          ;; for some users.
  2779.          (not vm-spooled-mail-waiting))
  2780.         (progn
  2781.           (setq oldval vm-spooled-mail-waiting)
  2782.           (vm-check-for-spooled-mail nil)
  2783.           (if (not (eq oldval vm-spooled-mail-waiting))
  2784.           (progn
  2785.             (intern (buffer-name) vm-buffers-needing-display-update)
  2786.             (vm-update-summary-and-mode-line))))))
  2787.       (setq b-list (cdr b-list)))
  2788.     ;; make the timer go away if we didn't encounter a vm-mode buffer.
  2789.     (if (and (not found-one) (null b-list))
  2790.     (if timer
  2791.         (cancel-timer timer)
  2792.       (set-itimer-restart current-itimer nil)))))
  2793.  
  2794. ;; support for numeric vm-auto-get-new-mail
  2795. ;; if timer argument is present, this means we're using the Emacs
  2796. ;; 'timer package rather than the 'itimer package.
  2797. (defun vm-get-mail-itimer-function (&optional timer)
  2798.   ;; FSF Emacs sets this non-nil, which means the user can't
  2799.   ;; interrupt mail retrieval.  Bogus.
  2800.   (setq inhibit-quit nil)
  2801.   (if (integerp vm-auto-get-new-mail)
  2802.       (if timer
  2803.       (timer-set-time timer (current-time) vm-auto-get-new-mail)
  2804.     (set-itimer-restart current-itimer vm-auto-get-new-mail))
  2805.     ;; user has changed the variable value to something that
  2806.     ;; isn't a number, make the timer go away.
  2807.     (if timer
  2808.     (cancel-timer timer)
  2809.       (set-itimer-restart current-itimer nil)))
  2810.   (let ((b-list (buffer-list))
  2811.     (found-one nil))
  2812.     (while (and (not (input-pending-p)) b-list)
  2813.       (save-excursion
  2814.     (set-buffer (car b-list))
  2815.     (if (and (eq major-mode 'vm-mode)
  2816.          (setq found-one t)
  2817.          (not (and (not (buffer-modified-p))
  2818.                buffer-file-name
  2819.                (file-newer-than-file-p
  2820.                 (make-auto-save-file-name)
  2821.                 buffer-file-name)))
  2822.          (not vm-block-new-mail)
  2823.          (not vm-folder-read-only)
  2824.          (vm-get-spooled-mail nil)
  2825.          (vm-assimilate-new-messages t))
  2826.         (progn
  2827.           ;; don't move the message pointer unless the folder
  2828.           ;; was empty.
  2829.           (if (and (null vm-message-pointer)
  2830.                (vm-thoughtfully-select-message))
  2831.           (vm-preview-current-message)
  2832.         (vm-update-summary-and-mode-line)))))
  2833.       (setq b-list (cdr b-list)))
  2834.     ;; make the timer go away if we didn't encounter a vm-mode buffer.
  2835.     (if (and (not found-one) (null b-list))
  2836.     (if timer
  2837.         (cancel-timer timer)
  2838.       (set-itimer-restart current-itimer nil)))))
  2839.  
  2840. ;; support for numeric vm-flush-interval
  2841. ;; if timer argument is present, this means we're using the Emacs
  2842. ;; 'timer package rather than the 'itimer package.
  2843. (defun vm-flush-itimer-function (&optional timer)
  2844.   (if (integerp vm-flush-interval)
  2845.       (if timer
  2846.       (timer-set-time timer (current-time) vm-flush-interval)
  2847.     (set-itimer-restart current-itimer vm-flush-interval)))
  2848.   ;; if no vm-mode buffers are found, we might as well shut down the
  2849.   ;; flush itimer.
  2850.   (if (not (vm-flush-cached-data))
  2851.       (if timer
  2852.       (cancel-timer timer)
  2853.     (set-itimer-restart current-itimer nil))))
  2854.  
  2855. ;; flush cached data in all vm-mode buffers.
  2856. ;; returns non-nil if any vm-mode buffers were found.
  2857. (defun vm-flush-cached-data ()
  2858.   (save-excursion
  2859.     (let ((buf-list (buffer-list))
  2860.       (found-one nil))
  2861.       (while (and buf-list (not (input-pending-p)))
  2862.     (set-buffer (car buf-list))
  2863.     (cond ((and (eq major-mode 'vm-mode) vm-message-list)
  2864.            (setq found-one t)
  2865.            (if (not (eq vm-modification-counter
  2866.                 vm-flushed-modification-counter))
  2867.            (progn
  2868.              (vm-stuff-summary)
  2869.              (vm-stuff-labels)
  2870.              (and vm-message-order-changed
  2871.               (vm-stuff-message-order))
  2872.              (and (vm-stuff-folder-attributes t)
  2873.               (setq vm-flushed-modification-counter
  2874.                 vm-modification-counter))))))
  2875.     (setq buf-list (cdr buf-list)))
  2876.       ;; if we haven't checked them all return non-nil so
  2877.       ;; the flusher won't give up trying.
  2878.       (or buf-list found-one) )))
  2879.  
  2880. ;; This allows C-x C-s to do the right thing for VM mail buffers.
  2881. ;; Note that deleted messages are not expunged.
  2882. (defun vm-write-file-hook ()
  2883.   (if (and (eq major-mode 'vm-mode) (not vm-inhibit-write-file-hook))
  2884.     ;; The vm-save-restriction isn't really necessary here, since
  2885.     ;; the stuff routines clean up after themselves, but should remain
  2886.     ;; as a safeguard against the time when other stuff is added here.
  2887.     (vm-save-restriction
  2888.      (let ((buffer-read-only))
  2889.        (vm-stuff-folder-attributes nil)
  2890.        (if vm-message-list
  2891.        (progn
  2892.          ;; get summary cache up-to-date
  2893.          (vm-update-summary-and-mode-line)
  2894.          (vm-stuff-bookmark)
  2895.          (vm-stuff-pop-retrieved)
  2896.          (vm-stuff-last-modified)
  2897.          (vm-stuff-header-variables)
  2898.          (vm-stuff-labels)
  2899.          (vm-stuff-summary)
  2900.          (and vm-message-order-changed
  2901.           (vm-stuff-message-order))))
  2902.        nil ))))
  2903.  
  2904. (defun vm-save-buffer (prefix)
  2905.   (interactive "P")
  2906.   (vm-select-folder-buffer)
  2907.   (vm-error-if-virtual-folder)
  2908.   (save-buffer prefix)
  2909.   (intern (buffer-name) vm-buffers-needing-display-update)
  2910.   (setq vm-block-new-mail nil)
  2911.   (vm-display nil nil '(vm-save-buffer) '(vm-save-buffer))
  2912.   (vm-update-summary-and-mode-line)
  2913.   (vm-write-index-file-maybe))
  2914.  
  2915. (defun vm-write-file ()
  2916.   (interactive)
  2917.   (vm-select-folder-buffer)
  2918.   (vm-error-if-virtual-folder)
  2919.   (let ((old-buffer-name (buffer-name)))
  2920.     (save-excursion
  2921.       (call-interactively 'write-file))
  2922.     (if (not (equal (buffer-name) old-buffer-name))
  2923.     (progn
  2924.       (vm-check-for-killed-summary)
  2925.       (if vm-summary-buffer
  2926.           (save-excursion
  2927.         (let ((name (buffer-name)))
  2928.           (set-buffer vm-summary-buffer)
  2929.           (rename-buffer (format "%s Summary" name) t))))
  2930.       (vm-check-for-killed-presentation)
  2931.       (if vm-presentation-buffer-handle
  2932.           (save-excursion
  2933.         (let ((name (buffer-name)))
  2934.           (set-buffer vm-presentation-buffer-handle)
  2935.           (rename-buffer (format "%s Presentation" name) t)))))))
  2936.   (intern (buffer-name) vm-buffers-needing-display-update)
  2937.   (setq vm-block-new-mail nil)
  2938.   (vm-display nil nil '(vm-write-file) '(vm-write-file))
  2939.   (vm-update-summary-and-mode-line)
  2940.   (vm-write-index-file-maybe))
  2941.  
  2942. (defun vm-unblock-new-mail ()
  2943.   (setq vm-block-new-mail nil))
  2944.  
  2945. (defun vm-save-folder (&optional prefix)
  2946.   "Save current folder to disk.
  2947. Deleted messages are not expunged.
  2948. Prefix arg is handled the same as for the command save-buffer.
  2949.  
  2950. When applied to a virtual folder, this command runs itself on
  2951. each of the underlying real folders associated with the virtual
  2952. folder."
  2953.   (interactive (list current-prefix-arg))
  2954.   (vm-select-folder-buffer)
  2955.   (vm-check-for-killed-summary)
  2956.   (vm-display nil nil '(vm-save-folder) '(vm-save-folder))
  2957.   (if (eq major-mode 'vm-virtual-mode)
  2958.       (vm-virtual-save-folder prefix)
  2959.     (if (buffer-modified-p)
  2960.     (let (mp (newlist nil))
  2961.       ;; stuff the attributes of messages that need it.
  2962.       (message "Stuffing attributes...")
  2963.       (vm-stuff-folder-attributes nil)
  2964.       ;; stuff bookmark and header variable values
  2965.       (if vm-message-list
  2966.           (progn
  2967.         ;; get summary cache up-to-date
  2968.         (vm-update-summary-and-mode-line)
  2969.         (vm-stuff-bookmark)
  2970.         (vm-stuff-pop-retrieved)
  2971.         (vm-stuff-last-modified)
  2972.         (vm-stuff-header-variables)
  2973.         (vm-stuff-labels)
  2974.         (vm-stuff-summary)
  2975.         (and vm-message-order-changed
  2976.              (vm-stuff-message-order))))
  2977.       (message "Saving...")
  2978.       (let ((vm-inhibit-write-file-hook t))
  2979.         (save-buffer prefix))
  2980.       (vm-set-buffer-modified-p nil)
  2981.       (vm-clear-modification-flag-undos)
  2982.       (setq vm-messages-not-on-disk 0)
  2983.       (setq vm-block-new-mail nil)
  2984.       (vm-write-index-file-maybe)
  2985.       (vm-update-summary-and-mode-line)
  2986.       (and (zerop (buffer-size))
  2987.            vm-delete-empty-folders
  2988.            buffer-file-name
  2989.            (or (eq vm-delete-empty-folders t)
  2990.            (y-or-n-p (format "%s is empty, remove it? "
  2991.                      (or buffer-file-name (buffer-name)))))
  2992.            (condition-case ()
  2993.            (progn
  2994.              (delete-file buffer-file-name)
  2995.              (vm-delete-index-file)
  2996.              (clear-visited-file-modtime)
  2997.              (message "%s removed" buffer-file-name))
  2998.          ;; no can do, oh well.
  2999.          (error nil))))
  3000.       (message "No changes need to be saved"))))
  3001.  
  3002. (defun vm-save-and-expunge-folder (&optional prefix)
  3003.   "Expunge folder, then save it to disk.
  3004. Prefix arg is handled the same as for the command save-buffer.
  3005. Expunge won't be done if folder is read-only.
  3006.  
  3007. When applied to a virtual folder, this command works as if you had
  3008. run vm-expunge-folder followed by vm-save-folder."
  3009.   (interactive (list current-prefix-arg))
  3010.   (vm-select-folder-buffer)
  3011.   (vm-check-for-killed-summary)
  3012.   (vm-display nil nil '(vm-save-and-expunge-folder)
  3013.           '(vm-save-and-expunge-folder))
  3014.   (if (not vm-folder-read-only)
  3015.       (progn
  3016.     (message "Expunging...")
  3017.     (vm-expunge-folder t)))
  3018.   (vm-save-folder prefix))
  3019.  
  3020. (defun vm-handle-file-recovery-or-reversion (recovery)
  3021.   (if (and vm-summary-buffer (buffer-name vm-summary-buffer))
  3022.       (kill-buffer vm-summary-buffer))
  3023.   (vm-virtual-quit)
  3024.   ;; reset major mode, this will cause vm to start from scratch.
  3025.   (setq major-mode 'fundamental-mode)
  3026.   ;; If this is a recovery, we can't allow the user to get new
  3027.   ;; mail until a real save is performed.  Until then the buffer
  3028.   ;; and the disk don't match.
  3029.   (if recovery
  3030.       (setq vm-block-new-mail t))
  3031.   (vm buffer-file-name))
  3032.  
  3033. ;; detect if a recover-file is being performed
  3034. ;; and handle things properly.
  3035. (defun vm-handle-file-recovery ()
  3036.   (if (and (buffer-modified-p)
  3037.        (eq major-mode 'vm-mode)
  3038.        vm-message-list
  3039.        (= (vm-end-of (car vm-message-list)) 1))
  3040.       (vm-handle-file-recovery-or-reversion t)))
  3041.  
  3042. ;; detect if a revert-buffer is being performed
  3043. ;; and handle things properly.
  3044. (defun vm-handle-file-reversion ()
  3045.   (if (and (not (buffer-modified-p))
  3046.        (eq major-mode 'vm-mode)
  3047.        vm-message-list
  3048.        (= (vm-end-of (car vm-message-list)) 1))
  3049.       (vm-handle-file-recovery-or-reversion nil)))
  3050.  
  3051. ;; FSF v19.23 revert-buffer doesn't mash all the markers together
  3052. ;; like v18 and prior v19 versions, so the check in
  3053. ;; vm-handle-file-reversion doesn't work.  However v19.23 has a
  3054. ;; hook we can use, after-revert-hook.
  3055. (defun vm-after-revert-buffer-hook ()
  3056.   (if (eq major-mode 'vm-mode)
  3057.       (vm-handle-file-recovery-or-reversion nil)))
  3058.  
  3059. (defun vm-help ()
  3060.   "Display help for various VM activities."
  3061.   (interactive)
  3062.   (if (eq major-mode 'vm-summary-mode)
  3063.       (vm-select-folder-buffer))
  3064.   (let ((pop-up-windows (and pop-up-windows (eq vm-mutable-windows t)))
  3065.     (pop-up-frames (and vm-mutable-frames vm-frame-per-help)))
  3066.     (cond
  3067.      ((eq last-command 'vm-help)
  3068.       (describe-function major-mode))
  3069.      ((eq vm-system-state 'previewing)
  3070.       (message "Type SPC to read message, n previews next message   (? gives more help)"))
  3071.      ((memq vm-system-state '(showing reading))
  3072.       (message "SPC and b scroll, (d)elete, (s)ave, (n)ext, (r)eply   (? gives more help)"))
  3073.      ((eq vm-system-state 'editing)
  3074.       (message 
  3075.        (substitute-command-keys
  3076.     "Type \\[vm-edit-message-end] to end edit, \\[vm-edit-message-abort] to abort with no change.")))
  3077.      ((eq major-mode 'mail-mode)
  3078.       (message
  3079.        (substitute-command-keys
  3080.     "Type \\[vm-mail-send-and-exit] to send message, \\[kill-buffer] to discard this composition")))
  3081.      (t (describe-mode)))))
  3082.  
  3083. (defun vm-spool-move-mail (source destination)
  3084.   (let ((handler (and (fboundp 'find-file-name-handler)
  3085.               (condition-case ()
  3086.               (find-file-name-handler source 'vm-spool-move-mail)
  3087.             (wrong-number-of-arguments
  3088.               (find-file-name-handler source)))))
  3089.     status error-buffer)
  3090.     (if handler
  3091.     (funcall handler 'vm-spool-move-mail source destination)
  3092.       (setq error-buffer
  3093.         (get-buffer-create
  3094.          (format "*output of %s %s %s*"
  3095.              vm-movemail-program source destination)))
  3096.       (save-excursion
  3097.     (set-buffer error-buffer)
  3098.     (erase-buffer))
  3099.       (setq status
  3100.         (call-process vm-movemail-program nil error-buffer t
  3101.               source destination))
  3102.       (save-excursion
  3103.     (set-buffer error-buffer)
  3104.     (if (and (numberp status) (not (= 0 status)))
  3105.         (insert (format "\n%s exited with code %s\n"
  3106.                 vm-movemail-program status)))
  3107.     (if (> (buffer-size) 0)
  3108.         (progn
  3109.           (vm-display-buffer error-buffer)
  3110.           (if (and (numberp status) (not (= 0 status)))
  3111.           (error "Failed getting new mail from %s" source)
  3112.         (message "Warning: unexpected output from %s"
  3113.              vm-movemail-program)
  3114.         (sleep-for 2)))
  3115.       ;; nag, nag, nag.
  3116.       (kill-buffer error-buffer))
  3117.     t ))))
  3118.  
  3119. (defun vm-gobble-crash-box (crash-box)
  3120.   (save-excursion
  3121.     (vm-save-restriction
  3122.      (widen)
  3123.      (let ((opoint-max (point-max)) crash-buf
  3124.        (buffer-read-only nil)
  3125.        (inbox-buffer-file buffer-file-name)
  3126.        (inbox-folder-type vm-folder-type)
  3127.        (inbox-empty (zerop (buffer-size)))
  3128.        got-mail crash-folder-type
  3129.        (old-buffer-modified-p (buffer-modified-p)))
  3130.        (setq crash-buf
  3131.          ;; crash box could contain a letter bomb...
  3132.          ;; force user notification of file variables for v18 Emacses
  3133.          ;; enable-local-variables == nil disables them for newer Emacses
  3134.          (let ((inhibit-local-variables t)
  3135.            (enable-local-variables nil)
  3136.            (coding-system-for-read 'no-conversion))
  3137.            (find-file-noselect crash-box)))
  3138.        (save-excursion
  3139.      (set-buffer crash-buf)
  3140.      (setq crash-folder-type (vm-get-folder-type))
  3141.      (if (and crash-folder-type vm-check-folder-types)
  3142.          (cond ((eq crash-folder-type 'unknown)
  3143.             (error "crash box %s's type is unrecognized" crash-box))
  3144.            ((eq inbox-folder-type 'unknown)
  3145.             (error "inbox %s's type is unrecognized"
  3146.                inbox-buffer-file))
  3147.            ((null inbox-folder-type)
  3148.             (if vm-default-folder-type
  3149.             (if (not (eq vm-default-folder-type
  3150.                      crash-folder-type))
  3151.                 (if vm-convert-folder-types
  3152.                 (progn
  3153.                   (vm-convert-folder-type
  3154.                    crash-folder-type
  3155.                    vm-default-folder-type)
  3156.                   ;; so that kill-buffer won't ask a
  3157.                   ;; question later...
  3158.                   (set-buffer-modified-p nil))
  3159.                   (error "crash box %s mismatches vm-default-folder-type: %s, %s"
  3160.                      crash-box crash-folder-type
  3161.                      vm-default-folder-type)))))
  3162.            ((not (eq inbox-folder-type crash-folder-type))
  3163.             (if vm-convert-folder-types
  3164.             (progn
  3165.               (vm-convert-folder-type crash-folder-type
  3166.                           inbox-folder-type)
  3167.               ;; so that kill-buffer won't ask a
  3168.               ;; question later...
  3169.               (set-buffer-modified-p nil))
  3170.               (error "crash box %s mismatches %s's folder type: %s, %s"
  3171.                  crash-box inbox-buffer-file
  3172.                  crash-folder-type inbox-folder-type)))))
  3173.      ;; toss the folder header if the inbox is not empty
  3174.      (goto-char (point-min))
  3175.      (if (not inbox-empty)
  3176.          (progn
  3177.            (vm-convert-folder-header (or inbox-folder-type
  3178.                          vm-default-folder-type)
  3179.                      nil)
  3180.            (set-buffer-modified-p nil))))
  3181.        (goto-char (point-max))
  3182.        (insert-buffer-substring crash-buf
  3183.                 1 (1+ (save-excursion
  3184.                     (set-buffer crash-buf)
  3185.                     (widen)
  3186.                     (buffer-size))))
  3187.        (setq got-mail (/= opoint-max (point-max)))
  3188.        (if (not got-mail)
  3189.        nil
  3190.      (write-region opoint-max (point-max) buffer-file-name t t)
  3191.      (vm-increment vm-modification-counter)
  3192.      (set-buffer-modified-p old-buffer-modified-p))
  3193.        (kill-buffer crash-buf)
  3194.        (if (not (stringp vm-keep-crash-boxes))
  3195.        (vm-error-free-call 'delete-file crash-box)
  3196.      (let ((time (decode-time (current-time)))
  3197.            name)
  3198.        (setq name
  3199.          (expand-file-name (format "Z-%02d-%02d-%05d"
  3200.                        (nth 4 time)
  3201.                        (nth 3 time)
  3202.                        (% (vm-abs (random)) 100000))
  3203.                    vm-keep-crash-boxes))
  3204.        (while (file-exists-p name)
  3205.          (setq name
  3206.            (expand-file-name (format "Z-%02d-%02d-%05d"
  3207.                          (nth 4 time)
  3208.                          (nth 3 time)
  3209.                          (% (vm-abs (random)) 100000))
  3210.                      vm-keep-crash-boxes)))
  3211.        (rename-file crash-box name)))
  3212.        got-mail ))))
  3213.  
  3214. (defun vm-compute-spool-files ()
  3215.   (let ((fallback-triples nil)
  3216.     triples)
  3217.     (cond ((and buffer-file-name
  3218.         (consp vm-spool-file-suffixes)
  3219.         (stringp vm-crash-box-suffix))
  3220.        (setq fallback-triples
  3221.          (mapcar (function
  3222.               (lambda (suffix)
  3223.                 (list buffer-file-name
  3224.                   (concat buffer-file-name suffix)
  3225.                   (concat buffer-file-name
  3226.                       vm-crash-box-suffix))))
  3227.              vm-spool-file-suffixes))))
  3228.     (cond ((and buffer-file-name
  3229.         vm-make-spool-file-name vm-make-crash-box-name)
  3230.        (setq fallback-triples
  3231.          (nconc fallback-triples
  3232.             (list (list buffer-file-name
  3233.                     (save-excursion
  3234.                       (funcall vm-make-spool-file-name
  3235.                            buffer-file-name))
  3236.                     (save-excursion
  3237.                       (funcall vm-make-crash-box-name
  3238.                            buffer-file-name))))))))
  3239.     (cond ((null (vm-spool-files))
  3240.        (setq triples (list
  3241.               (list vm-primary-inbox
  3242.                 (concat vm-spool-directory (user-login-name))
  3243.                 vm-crash-box))))
  3244.       ((stringp (car (vm-spool-files)))
  3245.        (setq triples
  3246.          (mapcar (function
  3247.               (lambda (s) (list vm-primary-inbox s vm-crash-box)))
  3248.              (vm-spool-files))))
  3249.       ((consp (car (vm-spool-files)))
  3250.        (setq triples (vm-spool-files))))
  3251.     (setq triples (append triples fallback-triples))
  3252.     triples ))
  3253.  
  3254. (defun vm-spool-check-mail (source)
  3255.   (let ((handler (and (fboundp 'find-file-name-handler)
  3256.               (condition-case ()
  3257.               (find-file-name-handler source 'vm-spool-check-mail)
  3258.             (wrong-number-of-arguments
  3259.              (find-file-name-handler source))))))
  3260.     (if handler
  3261.     (funcall handler 'vm-spool-check-mail source)
  3262.       (and (not (equal 0 (nth 7 (file-attributes source))))
  3263.        (file-readable-p source)))))
  3264.  
  3265. (defun vm-movemail-specific-spool-file-p (file)
  3266.   (string-match "^po:[^:]+$" file))
  3267.  
  3268. (defun vm-check-for-spooled-mail (&optional interactive)
  3269.   (if vm-block-new-mail
  3270.       nil
  3271.     (let ((triples (vm-compute-spool-files))
  3272.       ;; since we could accept-process-output here (POP code),
  3273.       ;; a timer process might try to start retrieving mail
  3274.       ;; before we finish.  block these attempts.
  3275.       (vm-block-new-mail t)
  3276.       (vm-pop-ok-to-ask interactive)
  3277.       (done nil)
  3278.       crash in maildrop popdrop
  3279.       (mail-waiting nil))
  3280.       (while (and triples (not done))
  3281.     (setq in (expand-file-name (nth 0 (car triples)) vm-folder-directory)
  3282.           maildrop (nth 1 (car triples))
  3283.           crash (nth 2 (car triples)))
  3284.     (if (vm-movemail-specific-spool-file-p maildrop)
  3285.         ;; spool file is accessible only with movemail
  3286.         ;; so skip it.
  3287.         nil
  3288.       (if (eq (current-buffer) (vm-get-file-buffer in))
  3289.           (progn
  3290.         (if (file-exists-p crash)
  3291.             (progn
  3292.               (setq mail-waiting t
  3293.                 done t))
  3294.           (setq popdrop (and vm-recognize-pop-maildrops
  3295.                      (string-match vm-recognize-pop-maildrops
  3296.                            maildrop)))
  3297.           (if (not interactive)
  3298.               ;; allow no error to be signaled
  3299.               (condition-case nil
  3300.               (setq mail-waiting
  3301.                 (or mail-waiting
  3302.                     (if popdrop
  3303.                     (vm-pop-check-mail maildrop)
  3304.                       (vm-spool-check-mail maildrop))))
  3305.             (error nil))
  3306.             (setq mail-waiting (or mail-waiting
  3307.                        (if popdrop
  3308.                            (vm-pop-check-mail maildrop)
  3309.                          (vm-spool-check-mail maildrop)))))
  3310.           (if mail-waiting
  3311.               (setq done t))))))
  3312.     (setq triples (cdr triples)))
  3313.       (setq vm-spooled-mail-waiting mail-waiting)
  3314.       mail-waiting )))
  3315.  
  3316. (defun vm-get-spooled-mail (&optional interactive)
  3317.   (if vm-block-new-mail
  3318.       (error "Can't get new mail until you save this folder."))
  3319.   (let ((triples (vm-compute-spool-files))
  3320.     ;; since we could accept-process-output here (POP code),
  3321.     ;; a timer process might try to start retrieving mail
  3322.     ;; before we finish.  block these attempts.
  3323.     (vm-block-new-mail t)
  3324.     (vm-pop-ok-to-ask interactive)
  3325.     crash in maildrop popdrop need-movemail
  3326.     (got-mail nil))
  3327.     (if (and (not (verify-visited-file-modtime (current-buffer)))
  3328.          (or (null interactive)
  3329.          (not (yes-or-no-p
  3330.                (format
  3331.             "Folder %s changed on disk, discard those changes? "
  3332.             (buffer-name (current-buffer)))))))
  3333.     (progn
  3334.       (message "Folder %s changed on disk, consider M-x revert-buffer"
  3335.            (buffer-name (current-buffer)))
  3336.       (sleep-for 2)
  3337.       nil )
  3338.       (while triples
  3339.     (setq in (expand-file-name (nth 0 (car triples)) vm-folder-directory)
  3340.           maildrop (nth 1 (car triples))
  3341.           crash (nth 2 (car triples))
  3342.           need-movemail (vm-movemail-specific-spool-file-p maildrop))
  3343.     (if (eq (current-buffer) (vm-get-file-buffer in))
  3344.         (let (retrieval-function)
  3345.           (if (file-exists-p crash)
  3346.           (progn
  3347.             (message "Recovering messages from %s..." crash)
  3348.             (setq got-mail (or (vm-gobble-crash-box crash) got-mail))
  3349.             (message "Recovering messages from %s... done" crash)))
  3350.           (setq popdrop (and (not need-movemail)
  3351.                  vm-recognize-pop-maildrops
  3352.                  (string-match vm-recognize-pop-maildrops
  3353.                            maildrop)
  3354.                  ;; maildrop with password clipped
  3355.                  (vm-safe-popdrop-string maildrop)))
  3356.           (if (or popdrop need-movemail
  3357.               (and (not (equal 0 (nth 7 (file-attributes maildrop))))
  3358.                (file-readable-p maildrop)))
  3359.           (progn
  3360.             (setq crash (expand-file-name crash vm-folder-directory))
  3361.             (cond (need-movemail
  3362.                (setq retrieval-function 'vm-spool-move-mail))
  3363.               ((not popdrop)
  3364.                (setq maildrop (expand-file-name maildrop)
  3365.                  retrieval-function 'vm-spool-move-mail))
  3366.               (t
  3367.                (setq retrieval-function 'vm-pop-move-mail)))
  3368.             (if (if got-mail
  3369.                 ;; don't allow errors to be signaled unless no
  3370.                 ;; mail has been appended to the incore
  3371.                 ;; copy of the folder.  otherwise the
  3372.                 ;; user will wonder where the mail is,
  3373.                 ;; since it is not in the crash box or
  3374.                 ;; the spool file and doesn't _appear_ to
  3375.                 ;; be in the folder either.
  3376.                 (condition-case error-data
  3377.                 (funcall retrieval-function maildrop crash)
  3378.                   (error (message "%s signaled: %s"
  3379.                           (if popdrop
  3380.                           'vm-pop-move-mail
  3381.                         'vm-spool-move-mail)
  3382.                           error-data)
  3383.                      (sleep-for 2)
  3384.                      ;; we don't know if mail was
  3385.                      ;; put into the crash box or
  3386.                      ;; not, so return t just to be
  3387.                      ;; safe.
  3388.                      t )
  3389.                   (quit (message "quitting from %s..."
  3390.                          (if popdrop
  3391.                          'vm-pop-move-mail
  3392.                            'vm-spool-move-mail))
  3393.                     (sleep-for 2)
  3394.                     ;; we don't know if mail was
  3395.                     ;; put into the crash box or
  3396.                     ;; not, so return t just to be
  3397.                     ;; safe.
  3398.                     t ))
  3399.               (funcall retrieval-function maildrop crash))
  3400.             (if (vm-gobble-crash-box crash)              
  3401.                 (progn
  3402.                   (setq got-mail t)
  3403.                   (message "Got mail from %s."
  3404.                        (or popdrop maildrop)))))))))
  3405.     (setq triples (cdr triples)))
  3406.       ;; not really correct, but it is what the user expects to see.
  3407.       (if got-mail
  3408.       (setq vm-spooled-mail-waiting nil))
  3409.       (intern (buffer-name) vm-buffers-needing-display-update)
  3410.       (vm-update-summary-and-mode-line)
  3411.       (if got-mail
  3412.       (run-hooks 'vm-retrieved-spooled-mail-hook))
  3413.       got-mail )))
  3414.  
  3415. (defun vm-safe-popdrop-string (drop)
  3416.   (or (and (string-match "^\\([^:]+\\):[^:]+:[^:]+:\\([^:]+\\):[^:]+" drop)
  3417.        (concat (substring drop (match-beginning 2) (match-end 2))
  3418.            "@"
  3419.            (substring drop (match-beginning 1) (match-end 1))))
  3420.       "???"))
  3421.  
  3422. (defun vm-get-new-mail (&optional arg)
  3423.   "Move any new mail that has arrived in any of the spool files for the
  3424. current folder into the folder.  New mail is appended to the disk
  3425. and buffer copies of the folder.
  3426.  
  3427. Prefix arg means to gather mail from a user specified folder, instead of
  3428. the usual spool files.  The file name will be read from the minibuffer.
  3429. Unlike when getting mail from a spool file, the source file is left
  3430. undisturbed after its messages have been copied.
  3431.  
  3432. When applied to a virtual folder, this command runs itself on
  3433. each of the underlying real folders associated with this virtual
  3434. folder.  A prefix argument has no effect when this command is
  3435. applied to virtual folder; mail is always gathered from the spool
  3436. files."
  3437.   (interactive "P")
  3438.   (vm-select-folder-buffer)
  3439.   (vm-check-for-killed-summary)
  3440.   (vm-error-if-folder-read-only)
  3441.   (cond ((eq major-mode 'vm-virtual-mode)
  3442.      (vm-virtual-get-new-mail))
  3443.     ((not (eq major-mode 'vm-mode))
  3444.      (error "Can't get mail for a non-VM folder buffer"))
  3445.     ((null arg)
  3446.      (if (not (eq major-mode 'vm-mode))
  3447.          (vm-mode))
  3448.      (if (consp (car (vm-spool-files)))
  3449.          (message "Checking for new mail for %s..."
  3450.               (or buffer-file-name (buffer-name)))
  3451.        (message "Checking for new mail..."))
  3452.      (let (totals-blurb)
  3453.        (if (and (vm-get-spooled-mail t) (vm-assimilate-new-messages t))
  3454.            (progn
  3455.          ;; say this NOW, before the non-previewers read
  3456.          ;; a message, alter the new message count and
  3457.          ;; confuse themselves.
  3458.          (setq totals-blurb (vm-emit-totals-blurb))
  3459.          (vm-display nil nil '(vm-get-new-mail) '(vm-get-new-mail))
  3460.          (if (vm-thoughtfully-select-message)
  3461.              (vm-preview-current-message)
  3462.            (vm-update-summary-and-mode-line))
  3463.          (message totals-blurb))
  3464.          (if (consp (car (vm-spool-files)))
  3465.          (message "No new mail for %s"
  3466.               (or buffer-file-name (buffer-name)))
  3467.            (message "No new mail."))
  3468.          (and (interactive-p) (sit-for 4) (message "")))))
  3469.     (t
  3470.      (let ((buffer-read-only nil)
  3471.            folder mcount totals-blurb)
  3472.        (setq folder (read-file-name "Gather mail from folder: "
  3473.                     vm-folder-directory t))
  3474.        (if (and vm-check-folder-types
  3475.             (not (vm-compatible-folder-p folder)))
  3476.            (error "Folder %s is not the same format as this folder."
  3477.               folder))
  3478.        (save-excursion
  3479.          (vm-save-restriction
  3480.           (widen)
  3481.           (goto-char (point-max))
  3482.           (let ((coding-system-for-read 'binary))
  3483.         (insert-file-contents folder))))
  3484.        (setq mcount (length vm-message-list))
  3485.        (if (vm-assimilate-new-messages)
  3486.            (progn
  3487.          ;; say this NOW, before the non-previewers read
  3488.          ;; a message, alter the new message count and
  3489.          ;; confuse themselves.
  3490.          (setq totals-blurb (vm-emit-totals-blurb))
  3491.          (vm-display nil nil '(vm-get-new-mail) '(vm-get-new-mail))
  3492.          (if (vm-thoughtfully-select-message)
  3493.              (vm-preview-current-message)
  3494.            (vm-update-summary-and-mode-line))
  3495.          (message totals-blurb)
  3496.          ;; The gathered messages are actually still on disk
  3497.          ;; unless the user deletes the folder himself.
  3498.          ;; However, users may not understand what happened if
  3499.          ;; the messages go away after a "quit, no save".
  3500.          (setq vm-messages-not-on-disk
  3501.                (+ vm-messages-not-on-disk
  3502.               (- (length vm-message-list)
  3503.                  mcount))))
  3504.          (message "No messages gathered."))))))
  3505.  
  3506. ;; returns non-nil if there were any new messages
  3507. (defun vm-assimilate-new-messages (&optional
  3508.                    dont-read-attributes
  3509.                    gobble-order
  3510.                    labels)
  3511.   (let ((tail-cons (vm-last vm-message-list))
  3512.     b-list new-messages)
  3513.     (save-excursion
  3514.       (vm-save-restriction
  3515.        (widen)
  3516.        (vm-build-message-list)
  3517.        (if (or (null tail-cons) (cdr tail-cons))
  3518.        (progn
  3519.          (setq vm-ml-sort-keys nil)
  3520.          (if dont-read-attributes
  3521.          (vm-set-default-attributes (cdr tail-cons))
  3522.            (vm-read-attributes (cdr tail-cons)))
  3523.          ;; Yuck.  This has to be done here instead of in the
  3524.          ;; vm function because this needs to be done before
  3525.          ;; any initial thread sort (so that if the thread
  3526.          ;; sort matches the saved order the folder won't be
  3527.          ;; modified) but after the message list is created.
  3528.          ;; Since thread sorting is done here this has to be
  3529.          ;; done here too.
  3530.          (if gobble-order
  3531.          (vm-gobble-message-order))
  3532.          (if vm-thread-obarray
  3533.          (vm-build-threads (cdr tail-cons))))))
  3534.       (setq new-messages (if tail-cons (cdr tail-cons) vm-message-list))
  3535.       (vm-set-numbering-redo-start-point new-messages)
  3536.       (vm-set-summary-redo-start-point new-messages))
  3537.     ;; copy the new-messages list because sorting might scramble
  3538.     ;; it.  Also something the user does when
  3539.     ;; vm-arrived-message-hook is run might affect it.
  3540.     ;; vm-assimilate-new-messages returns this value so it must
  3541.     ;; not be mangled.
  3542.     (setq new-messages (copy-sequence new-messages))
  3543.     ;; add the labels
  3544.     (if (and labels vm-burst-digest-messages-inherit-labels)
  3545.     (let ((mp new-messages))
  3546.       (while mp
  3547.         (vm-set-labels-of (car mp) (copy-sequence labels))
  3548.         (setq mp (cdr mp)))))
  3549.     (if vm-summary-show-threads
  3550.     (progn
  3551.       ;; get numbering and summary of new messages done now
  3552.       ;; so that the sort code only has to worry about the
  3553.       ;; changes it needs to make.
  3554.       (vm-update-summary-and-mode-line)
  3555.       (vm-sort-messages "thread")))
  3556.     (if (and vm-arrived-message-hook
  3557.          new-messages
  3558.          ;; tail-cons == nil means vm-message-list was empty.
  3559.          ;; Thus new-messages == vm-message-list.  In this
  3560.          ;; case, run the hooks only if this is not the first
  3561.          ;; time vm-assimilate-new-messages has been called
  3562.          ;; in this folder.  gobble-order non-nil is a good
  3563.          ;; indicator that this is the first time because the
  3564.          ;; order is gobbled only once per visit and always
  3565.          ;; the first time vm-assimilate-new-messages is
  3566.          ;; called.
  3567.          (or tail-cons (null gobble-order)))
  3568.     (let ((new-messages new-messages))
  3569.       ;; seems wise to do this so that if the user runs VM
  3570.       ;; command here they start with as much of a clean
  3571.       ;; slate as we can provide, given we're currently deep
  3572.       ;; in the guts of VM.
  3573.       (vm-update-summary-and-mode-line)
  3574.       (while new-messages
  3575.         (vm-run-message-hook (car new-messages) 'vm-arrived-message-hook)
  3576.         (setq new-messages (cdr new-messages)))))
  3577.     (vm-update-summary-and-mode-line)
  3578.     (run-hooks 'vm-arrived-messages-hook)
  3579.     (if (and new-messages vm-virtual-buffers)
  3580.     (save-excursion
  3581.       (setq b-list vm-virtual-buffers)
  3582.       (while b-list
  3583.         ;; buffer might be dead
  3584.         (if (buffer-name (car b-list))
  3585.         (let (tail-cons)
  3586.           (set-buffer (car b-list))
  3587.           (setq tail-cons (vm-last vm-message-list))
  3588.           (vm-build-virtual-message-list new-messages)
  3589.           (if (or (null tail-cons) (cdr tail-cons))
  3590.               (progn
  3591.             (setq vm-ml-sort-keys nil)
  3592.             (if vm-thread-obarray
  3593.                 (vm-build-threads (cdr tail-cons)))
  3594.             (vm-set-summary-redo-start-point
  3595.              (or (cdr tail-cons) vm-message-list))
  3596.             (vm-set-numbering-redo-start-point
  3597.              (or (cdr tail-cons) vm-message-list))
  3598.             (if (null vm-message-pointer)
  3599.                 (progn (setq vm-message-pointer vm-message-list
  3600.                      vm-need-summary-pointer-update t)
  3601.                    (if vm-message-pointer
  3602.                        (vm-preview-current-message))))
  3603.             (if vm-summary-show-threads
  3604.                 (progn
  3605.                   (vm-update-summary-and-mode-line)
  3606.                   (vm-sort-messages "thread")))))))
  3607.         (setq b-list (cdr b-list)))))
  3608.     new-messages ))
  3609.  
  3610. ;; return a list of all marked messages or the messages indicated by a
  3611. ;; prefix argument.
  3612. (defun vm-select-marked-or-prefixed-messages (prefix)
  3613.   (let (mlist)
  3614.     (if (eq last-command 'vm-next-command-uses-marks)
  3615.     (setq mlist (vm-marked-messages))
  3616.       (let ((direction (if (< prefix 0) 'backward 'forward))
  3617.          (count (vm-abs prefix))
  3618.          (vm-message-pointer vm-message-pointer))
  3619.     (if (not (eq vm-circular-folders t))
  3620.         (vm-check-count prefix))
  3621.     (while (not (zerop count))
  3622.       (setq mlist (cons (car vm-message-pointer) mlist))
  3623.       (vm-decrement count)
  3624.       (if (not (zerop count))
  3625.           (vm-move-message-pointer direction))))
  3626.       (nreverse mlist))))
  3627.  
  3628. (defun vm-display-startup-message ()
  3629.   (if (sit-for 5)
  3630.       (let ((lines vm-startup-message-lines))
  3631.     (message "VM %s, Copyright (C) 1998 Kyle E. Jones; type ? for help"
  3632.          vm-version)
  3633.     (setq vm-startup-message-displayed t)
  3634.     (while (and (sit-for 4) lines)
  3635.       (message (substitute-command-keys (car lines)))
  3636.       (setq lines (cdr lines)))))
  3637.   (message ""))
  3638.  
  3639. (defun vm-load-init-file (&optional interactive)
  3640.   (interactive "p")
  3641.   (if (or (not vm-init-file-loaded) interactive)
  3642.       (progn
  3643.     (and vm-init-file
  3644.          (load vm-init-file (not interactive) (not interactive) t))
  3645.     (and vm-preferences-file (load vm-preferences-file t t t))))
  3646.   (setq vm-init-file-loaded t)
  3647.   (vm-display nil nil '(vm-load-init-file) '(vm-load-init-file)))
  3648.  
  3649. (defun vm-session-initialization ()
  3650.   ;; If this is the first time VM has been run in this Emacs session,
  3651.   ;; do some necessary preparations.
  3652.   (if (or (not (boundp 'vm-session-beginning))
  3653.       vm-session-beginning)
  3654.       (progn
  3655.     (random t)
  3656.     (vm-load-init-file)
  3657.     (if (not vm-window-configuration-file)
  3658.         (setq vm-window-configurations vm-default-window-configuration)
  3659.       (or (vm-load-window-configurations vm-window-configuration-file)
  3660.           (setq vm-window-configurations vm-default-window-configuration)))
  3661.     (setq vm-buffers-needing-display-update (make-vector 29 0))
  3662.     (setq vm-session-beginning nil))))
  3663.  
  3664. (defun vm-toggle-read-only ()
  3665.   (interactive)
  3666.   (vm-select-folder-buffer)
  3667.   (setq vm-folder-read-only (not vm-folder-read-only))
  3668.   (intern (buffer-name) vm-buffers-needing-display-update)
  3669.   (message "Folder is now %s"
  3670.        (if vm-folder-read-only "read-only" "modifiable"))
  3671.   (vm-display nil nil '(vm-toggle-read-only) '(vm-toggle-read-only))
  3672.   (vm-update-summary-and-mode-line))
  3673.  
  3674. (defvar scroll-in-place)
  3675.  
  3676. ;; this does the real major mode scutwork.
  3677. (defun vm-mode-internal ()
  3678.   (widen)
  3679.   (make-local-variable 'require-final-newline)
  3680.   ;; don't kill local variables, as there is some state we'd like to
  3681.   ;; keep.  rather than non-portably marking the variables we
  3682.   ;; want to keep, just avoid calling kill-local-variables and
  3683.   ;; reset everything that needs to be reset.
  3684.   (setq
  3685.    major-mode 'vm-mode
  3686.    mode-line-format vm-mode-line-format
  3687.    mode-name "VM"
  3688.    ;; must come after the setting of major-mode
  3689.    mode-popup-menu (and vm-use-menus vm-popup-menu-on-mouse-3
  3690.             (vm-menu-support-possible-p)
  3691.             (vm-menu-mode-menu))
  3692.    buffer-read-only t
  3693.    ;; If the user quits a vm-mode buffer, the default action is
  3694.    ;; to kill the buffer.  Make a note that we should offer to
  3695.    ;; save this buffer even if it has no file associated with it.
  3696.    ;; We have no idea of the value of the data in the buffer
  3697.    ;; before it was put into vm-mode.
  3698.    buffer-offer-save t
  3699.    require-final-newline nil
  3700.    vm-thread-obarray nil
  3701.    vm-thread-subject-obarray nil
  3702.    vm-label-obarray (make-vector 29 0)
  3703.    vm-last-message-pointer nil
  3704.    vm-modification-counter 0
  3705.    vm-message-list nil
  3706.    vm-message-pointer nil
  3707.    vm-message-order-changed nil
  3708.    vm-message-order-header-present nil
  3709.    vm-pop-retrieved-messages nil
  3710.    vm-summary-buffer nil
  3711.    vm-system-state nil
  3712.    vm-undo-record-list nil
  3713.    vm-undo-record-pointer nil
  3714.    vm-virtual-buffers (vm-link-to-virtual-buffers)
  3715.    vm-folder-type (vm-get-folder-type))
  3716.   (use-local-map vm-mode-map)
  3717.   ;; if the user saves after M-x recover-file, let them get new
  3718.   ;; mail again.
  3719.   (add-hook 'after-save-hook 'vm-unblock-new-mail)
  3720.   (and (vm-menu-support-possible-p)
  3721.        (vm-menu-install-menus))
  3722.   (add-hook 'kill-buffer-hook 'vm-garbage-collect-folder)
  3723.   (add-hook 'kill-buffer-hook 'vm-garbage-collect-message)
  3724.   ;; avoid the XEmacs file dialog box.
  3725.   (defvar use-dialog-box)
  3726.   (make-local-variable 'use-dialog-box)
  3727.   (setq use-dialog-box nil)
  3728.   ;; mail folders are precious.  protect them by default.
  3729.   (make-local-variable 'file-precious-flag)
  3730.   (setq file-precious-flag t)
  3731.   ;; scroll in place messes with scroll-up and this loses
  3732.   (make-local-variable 'scroll-in-place)
  3733.   (setq scroll-in-place nil)
  3734.   (run-hooks 'vm-mode-hook)
  3735.   ;; compatibility
  3736.   (run-hooks 'vm-mode-hooks))
  3737.  
  3738. (defun vm-link-to-virtual-buffers ()
  3739.   (let ((b-list (buffer-list))
  3740.     (vbuffers nil)
  3741.     (folder-buffer (current-buffer))
  3742.     folders clauses)
  3743.     (save-excursion
  3744.       (while b-list
  3745.     (set-buffer (car b-list))
  3746.     (cond ((eq major-mode 'vm-virtual-mode)
  3747.            (setq clauses (cdr vm-virtual-folder-definition))
  3748.            (while clauses
  3749.          (setq folders (car (car clauses)))
  3750.          (while folders
  3751.            (if (eq folder-buffer (vm-get-file-buffer
  3752.                       (expand-file-name
  3753.                        (car folders)
  3754.                        vm-folder-directory)))
  3755.                (setq vbuffers (cons (car b-list) vbuffers)
  3756.                  vm-real-buffers (cons folder-buffer
  3757.                            vm-real-buffers)
  3758.                  folders nil
  3759.                  clauses nil))
  3760.            (setq folders (cdr folders)))
  3761.          (setq clauses (cdr clauses)))))
  3762.     (setq b-list (cdr b-list)))
  3763.       vbuffers )))
  3764.  
  3765. (defun vm-change-folder-type (type)
  3766.   "Change folder type to TYPE.
  3767. TYPE may be one of the following symbol values:
  3768.  
  3769.     From_
  3770.     From_-with-Content-Length
  3771.     mmdf
  3772.     babyl
  3773.  
  3774. Interactively TYPE will be read from the minibuffer."
  3775.   (interactive
  3776.    (let ((this-command this-command)
  3777.      (last-command last-command)
  3778.      (types vm-supported-folder-types))
  3779.      (vm-select-folder-buffer)
  3780.      (vm-error-if-virtual-folder)
  3781.      (setq types (vm-delqual (symbol-name vm-folder-type)
  3782.                  (copy-sequence types)))
  3783.      (list (intern (vm-read-string "Change folder to type: " types)))))
  3784.   (vm-select-folder-buffer)
  3785.   (vm-check-for-killed-summary)
  3786.   (vm-error-if-virtual-folder)
  3787.   (vm-error-if-folder-empty)
  3788.   (if (not (memq type '(From_ From_-with-Content-Length mmdf babyl)))
  3789.       (error "Unknown folder type: %s" type))
  3790.   (if (or (null vm-folder-type)
  3791.       (eq vm-folder-type 'unknown))
  3792.       (error "Current folder's type is unknown, can't change it."))
  3793.   (let ((mp vm-message-list)
  3794.     (buffer-read-only nil)
  3795.     (old-type vm-folder-type)
  3796.     ;; no interruptions
  3797.     (inhibit-quit t)
  3798.     (n 0)
  3799.     ;; Just for laughs, make the update interval vary.
  3800.     (modulus (+ (% (vm-abs (random)) 11) 5))
  3801.     text-end opoint)
  3802.     (save-excursion
  3803.       (vm-save-restriction
  3804.        (widen)
  3805.        (setq vm-folder-type type)
  3806.        (goto-char (point-min))
  3807.        (vm-convert-folder-header old-type type)
  3808.        (while mp
  3809.      (goto-char (vm-start-of (car mp)))
  3810.      (setq opoint (point))
  3811.      (insert (vm-leading-message-separator type (car mp)))
  3812.      (if (> (vm-headers-of (car mp)) (vm-start-of (car mp)))
  3813.          (delete-region (point) (vm-headers-of (car mp)))
  3814.        (set-marker (vm-headers-of (car mp)) (point))
  3815.        ;; if headers-of == start-of then so could vheaders-of
  3816.        ;; and text-of.  clear them to force a recompute.
  3817.        (vm-set-vheaders-of (car mp) nil)
  3818.        (vm-set-text-of (car mp) nil))
  3819.      (vm-convert-folder-type-headers old-type type)
  3820.      (goto-char (vm-text-end-of (car mp)))
  3821.      (setq text-end (point))
  3822.      (insert-before-markers (vm-trailing-message-separator type))
  3823.      (delete-region (vm-text-end-of (car mp)) (vm-end-of (car mp)))
  3824.      (set-marker (vm-text-end-of (car mp)) text-end)
  3825.      (goto-char (vm-headers-of (car mp)))
  3826.      (vm-munge-message-separators type (vm-headers-of (car mp))
  3827.                       (vm-text-end-of (car mp)))
  3828.      (vm-set-byte-count-of (car mp) nil)
  3829.      (vm-set-babyl-frob-flag-of (car mp) nil)
  3830.      (vm-set-message-type-of (car mp) type)
  3831.      ;; Technically we should mark each message for a
  3832.      ;; summary update since the message byte counts might
  3833.      ;; have changed.  But I don't think anyone cares that
  3834.      ;; much and the summary regeneration would make this
  3835.      ;; process slower.
  3836.      (setq mp (cdr mp) n (1+ n))
  3837.      (if (zerop (% n modulus))
  3838.          (message "Converting... %d" n))))))
  3839.   (vm-clear-modification-flag-undos)
  3840.   (intern (buffer-name) vm-buffers-needing-display-update)
  3841.   (vm-update-summary-and-mode-line)
  3842.   (message "Conversion complete.")
  3843.   ;; message separator strings may have leaked into view
  3844.   (if (> (point-max) (vm-text-end-of (car vm-message-pointer)))
  3845.       (narrow-to-region (point-min) (vm-text-end-of (car vm-message-pointer))))
  3846.   (vm-display nil nil '(vm-change-folder-type) '(vm-change-folder-type)))
  3847.  
  3848. (defun vm-garbage-collect-folder ()
  3849.   (save-excursion
  3850.     (while vm-folder-garbage-alist
  3851.       (condition-case nil
  3852.       (funcall (cdr (car vm-folder-garbage-alist))
  3853.            (car (car vm-folder-garbage-alist)))
  3854.     (error nil))
  3855.       (setq vm-folder-garbage-alist (cdr vm-folder-garbage-alist)))))
  3856.  
  3857. (defun vm-garbage-collect-message ()
  3858.   (save-excursion
  3859.     (while vm-message-garbage-alist
  3860.       (condition-case nil
  3861.       (funcall (cdr (car vm-message-garbage-alist))
  3862.            (car (car vm-message-garbage-alist)))
  3863.     (error nil))
  3864.       (setq vm-message-garbage-alist (cdr vm-message-garbage-alist)))))
  3865.  
  3866. (if (not (memq 'vm-write-file-hook write-file-hooks))
  3867.     (setq write-file-hooks
  3868.       (cons 'vm-write-file-hook write-file-hooks)))
  3869.  
  3870. (if (not (memq 'vm-handle-file-recovery find-file-hooks))
  3871.     (setq find-file-hooks
  3872.       (nconc find-file-hooks
  3873.          '(vm-handle-file-recovery
  3874.            vm-handle-file-reversion))))
  3875.  
  3876. ;; after-revert-hook is new to FSF v19.23
  3877. (defvar after-revert-hook)
  3878. (if (boundp 'after-revert-hook)
  3879.     (setq after-revert-hook
  3880.       (cons 'vm-after-revert-buffer-hook after-revert-hook))
  3881.   (setq after-revert-hook (list 'vm-after-revert-buffer-hook)))
  3882.